mirror of
https://git.mirrors.martin98.com/https://github.com/luc-github/ESP3D.git
synced 2025-08-02 06:40:41 +08:00
Fix compilation for Async
Update library for ESP32 async
This commit is contained in:
parent
40c5156be5
commit
df51e025ce
@ -12,8 +12,11 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/me-no-dev/AsyncTCP.git"
|
||||
},
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.3",
|
||||
"license": "LGPL-3.0",
|
||||
"frameworks": "arduino",
|
||||
"platforms": ["espressif32", "espressif32_stage"]
|
||||
"platforms": "espressif32",
|
||||
"build": {
|
||||
"libCompatMode": 2
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
name=AsyncTCP
|
||||
version=1.0.0
|
||||
version=1.0.3
|
||||
author=Me-No-Dev
|
||||
maintainer=Me-No-Dev
|
||||
sentence=Async TCP Library for ESP32
|
||||
|
@ -29,12 +29,18 @@ extern "C"{
|
||||
#include "lwip/dns.h"
|
||||
}
|
||||
|
||||
#if CONFIG_FREERTOS_UNICORE
|
||||
#define ASYNCTCP_RUNNING_CORE 0
|
||||
#else
|
||||
#define ASYNCTCP_RUNNING_CORE 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TCP/IP Event Task
|
||||
* */
|
||||
|
||||
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;
|
||||
|
||||
typedef struct {
|
||||
@ -74,13 +80,77 @@ typedef struct {
|
||||
static xQueueHandle _async_queue;
|
||||
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);
|
||||
} 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);
|
||||
} 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);
|
||||
} else if(e->event == LWIP_TCP_ERROR){
|
||||
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){
|
||||
lwip_event_packet_t * packet = NULL;
|
||||
for (;;) {
|
||||
if(xQueueReceive(_async_queue, &packet, 0) == pdTRUE){
|
||||
//dispatch packet
|
||||
if(_get_async_event(&packet)){
|
||||
_handle_async_event(packet);
|
||||
} else {
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
@ -110,14 +177,11 @@ static void _stop_async_task(){
|
||||
}
|
||||
*/
|
||||
static bool _start_async_task(){
|
||||
if(!_async_queue){
|
||||
_async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t *));
|
||||
if(!_async_queue){
|
||||
if(!_init_async_event_queue()){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
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){
|
||||
return false;
|
||||
}
|
||||
@ -129,60 +193,58 @@ static bool _start_async_task(){
|
||||
* LwIP Callbacks
|
||||
* */
|
||||
|
||||
static int8_t _tcp_poll(void * arg, struct tcp_pcb * pcb) {
|
||||
if(!_async_queue){
|
||||
static int8_t _tcp_clear_events(void * arg) {
|
||||
lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
|
||||
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));
|
||||
e->event = LWIP_TCP_POLL;
|
||||
e->arg = arg;
|
||||
e->poll.pcb = pcb;
|
||||
if (xQueueSend(_async_queue, &e, portMAX_DELAY) != pdPASS) {
|
||||
if (!_send_async_event(&e)) {
|
||||
free((void*)(e));
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
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));
|
||||
e->event = LWIP_TCP_RECV;
|
||||
e->arg = arg;
|
||||
e->recv.pcb = pcb;
|
||||
e->recv.pb = pb;
|
||||
e->recv.err = err;
|
||||
if (xQueueSend(_async_queue, &e, portMAX_DELAY) != pdPASS) {
|
||||
if (!_send_async_event(&e)) {
|
||||
free((void*)(e));
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
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));
|
||||
e->event = LWIP_TCP_SENT;
|
||||
e->arg = arg;
|
||||
e->sent.pcb = pcb;
|
||||
e->sent.len = len;
|
||||
if (xQueueSend(_async_queue, &e, portMAX_DELAY) != pdPASS) {
|
||||
if (!_send_async_event(&e)) {
|
||||
free((void*)(e));
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
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));
|
||||
e->event = LWIP_TCP_ERROR;
|
||||
e->arg = arg;
|
||||
e->error.err = err;
|
||||
if (xQueueSend(_async_queue, &e, portMAX_DELAY) != pdPASS) {
|
||||
if (!_send_async_event(&e)) {
|
||||
free((void*)(e));
|
||||
}
|
||||
}
|
||||
@ -194,7 +256,7 @@ static void _tcp_error(void * arg, int8_t err) {
|
||||
#include "lwip/priv/tcpip_priv.h"
|
||||
|
||||
typedef struct {
|
||||
struct tcpip_api_call call;
|
||||
struct tcpip_api_call_data call;
|
||||
tcp_pcb * pcb;
|
||||
int8_t err;
|
||||
union {
|
||||
@ -217,20 +279,24 @@ typedef struct {
|
||||
};
|
||||
} 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;
|
||||
if(msg->pcb){
|
||||
msg->err = tcp_output(msg->pcb);
|
||||
} else {
|
||||
msg->err = 0;
|
||||
}
|
||||
return msg->err;
|
||||
}
|
||||
|
||||
static esp_err_t _tcp_output(tcp_pcb * pcb) {
|
||||
tcp_api_call_t msg;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
msg->err = tcp_write(msg->pcb, msg->write.data, msg->write.size, msg->write.apiflags);
|
||||
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.size = size;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
msg->err = 0;
|
||||
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;
|
||||
msg.pcb = pcb;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
msg->err = tcp_connect(msg->pcb, msg->connect.addr, msg->connect.port, msg->connect.cb);
|
||||
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.port = port;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
msg->err = tcp_close(msg->pcb);
|
||||
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) {
|
||||
tcp_api_call_t msg;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
msg->err = 0;
|
||||
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) {
|
||||
tcp_api_call_t msg;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
msg->err = tcp_bind(msg->pcb, msg->bind.addr, msg->bind.port);
|
||||
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.bind.addr = addr;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
msg->err = 0;
|
||||
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;
|
||||
msg.pcb = pcb;
|
||||
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;
|
||||
}
|
||||
#define _tcp_listen(p) _tcp_listen_with_backlog(p, 0xFF);
|
||||
@ -352,6 +420,8 @@ AsyncClient::AsyncClient(tcp_pcb* pcb)
|
||||
, _error_cb_arg(0)
|
||||
, _recv_cb(0)
|
||||
, _recv_cb_arg(0)
|
||||
, _pb_cb(0)
|
||||
, _pb_cb_arg(0)
|
||||
, _timeout_cb(0)
|
||||
, _timeout_cb_arg(0)
|
||||
, _pcb_busy(false)
|
||||
@ -366,6 +436,8 @@ AsyncClient::AsyncClient(tcp_pcb* pcb)
|
||||
, next(NULL)
|
||||
, _in_lwip_thread(false)
|
||||
{
|
||||
//ets_printf("+: 0x%08x\n", (uint32_t)this);
|
||||
|
||||
_pcb = pcb;
|
||||
if(_pcb){
|
||||
_rx_last_packet = millis();
|
||||
@ -374,12 +446,15 @@ AsyncClient::AsyncClient(tcp_pcb* pcb)
|
||||
tcp_sent(_pcb, &_tcp_sent);
|
||||
tcp_err(_pcb, &_tcp_error);
|
||||
tcp_poll(_pcb, &_tcp_poll, 1);
|
||||
//ets_printf("accept 0x%08x\n", (uint32_t)_pcb);
|
||||
}
|
||||
}
|
||||
|
||||
AsyncClient::~AsyncClient(){
|
||||
if(_pcb)
|
||||
_close();
|
||||
|
||||
//ets_printf("-: 0x%08x\n", (uint32_t)this);
|
||||
}
|
||||
|
||||
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(){
|
||||
//ets_printf("X: 0x%08x\n", (uint32_t)this);
|
||||
int8_t err = ERR_OK;
|
||||
if(_pcb) {
|
||||
//log_i("");
|
||||
@ -453,6 +529,7 @@ int8_t AsyncClient::_close(){
|
||||
tcp_recv(_pcb, NULL);
|
||||
tcp_err(_pcb, NULL);
|
||||
tcp_poll(_pcb, NULL, 0);
|
||||
_tcp_clear_events(this);
|
||||
if(_in_lwip_thread){
|
||||
err = tcp_close(_pcb);
|
||||
} else {
|
||||
@ -484,6 +561,7 @@ void AsyncClient::_error(int8_t err) {
|
||||
}
|
||||
|
||||
int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) {
|
||||
_in_lwip_thread = false;
|
||||
_rx_last_packet = millis();
|
||||
//log_i("%u", len);
|
||||
_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) {
|
||||
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){
|
||||
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);
|
||||
_ack_pcb = true;
|
||||
pbuf *b = pb;
|
||||
pb = b->next;
|
||||
b->next = NULL;
|
||||
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);
|
||||
pb = b->next;
|
||||
b->next = NULL;
|
||||
pbuf_free(b);
|
||||
}
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
int8_t AsyncClient::_poll(tcp_pcb* pcb){
|
||||
_in_lwip_thread = false;
|
||||
// Close requested
|
||||
if(_close_pcb){
|
||||
_close_pcb = false;
|
||||
@ -546,7 +637,7 @@ int8_t AsyncClient::_poll(tcp_pcb* pcb){
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
void AsyncClient::_dns_found(ip_addr_t *ipaddr){
|
||||
void AsyncClient::_dns_found(struct ip_addr *ipaddr){
|
||||
_in_lwip_thread = true;
|
||||
if(ipaddr){
|
||||
connect(IPAddress(ipaddr->u_addr.ip4.addr), _connect_port);
|
||||
@ -591,11 +682,13 @@ int8_t AsyncClient::abort(){
|
||||
}
|
||||
|
||||
void AsyncClient::close(bool now){
|
||||
if(_pcb){
|
||||
if(_in_lwip_thread){
|
||||
tcp_recved(_pcb, _rx_ack_len);
|
||||
} else {
|
||||
_tcp_recved(_pcb, _rx_ack_len);
|
||||
}
|
||||
}
|
||||
if(now)
|
||||
_close();
|
||||
else
|
||||
@ -814,6 +907,14 @@ bool AsyncClient::canSend(){
|
||||
return space() > 0;
|
||||
}
|
||||
|
||||
void AsyncClient::ackPacket(struct pbuf * pb){
|
||||
if(!pb){
|
||||
return;
|
||||
}
|
||||
_tcp_recved(_pcb, pb->len);
|
||||
pbuf_free(pb);
|
||||
}
|
||||
|
||||
|
||||
// Callback Setters
|
||||
|
||||
@ -842,6 +943,11 @@ void AsyncClient::onData(AcDataHandler cb, void* 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){
|
||||
_timeout_cb = cb;
|
||||
_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){
|
||||
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) {
|
||||
if(arg && pcb){
|
||||
reinterpret_cast<AsyncClient*>(arg)->_poll(pcb);
|
||||
} else {
|
||||
log_e("Bad Args: 0x%08x 0x%08x", arg, pcb);
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
int8_t AsyncClient::_s_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t 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;
|
||||
}
|
||||
|
||||
int8_t AsyncClient::_s_sent(void * arg, struct tcp_pcb * pcb, uint16_t 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;
|
||||
}
|
||||
|
||||
void AsyncClient::_s_error(void * arg, int8_t 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){
|
||||
if(arg && pcb){
|
||||
reinterpret_cast<AsyncClient*>(arg)->_connected(pcb, err);
|
||||
} else {
|
||||
log_e("Bad Args: 0x%08x 0x%08x", arg, pcb);
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
@ -1032,9 +1165,9 @@ void AsyncServer::end(){
|
||||
tcp_arg(_pcb, NULL);
|
||||
tcp_accept(_pcb, NULL);
|
||||
if(_in_lwip_thread){
|
||||
tcp_abort(_pcb);
|
||||
tcp_close(_pcb);
|
||||
} else {
|
||||
_tcp_abort(_pcb);
|
||||
_tcp_close(_pcb);
|
||||
}
|
||||
_pcb = NULL;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <functional>
|
||||
extern "C" {
|
||||
#include "freertos/semphr.h"
|
||||
#include "lwip/pbuf.h"
|
||||
}
|
||||
|
||||
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*, int8_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;
|
||||
|
||||
struct tcp_pcb;
|
||||
struct pbuf;
|
||||
struct _ip_addr;
|
||||
struct ip_addr;
|
||||
|
||||
class AsyncClient {
|
||||
protected:
|
||||
@ -58,6 +59,8 @@ class AsyncClient {
|
||||
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;
|
||||
@ -78,7 +81,7 @@ class AsyncClient {
|
||||
void _error(int8_t err);
|
||||
int8_t _poll(tcp_pcb* pcb);
|
||||
int8_t _sent(tcp_pcb* pcb, uint16_t len);
|
||||
void _dns_found(struct _ip_addr *ipaddr);
|
||||
void _dns_found(struct ip_addr *ipaddr);
|
||||
|
||||
|
||||
public:
|
||||
@ -105,13 +108,13 @@ class AsyncClient {
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
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();
|
||||
bool connecting();
|
||||
@ -141,10 +144,13 @@ class AsyncClient {
|
||||
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
|
||||
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(int8_t error);
|
||||
const char * stateToString();
|
||||
|
||||
@ -155,7 +161,7 @@ class AsyncClient {
|
||||
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_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;
|
||||
};
|
||||
|
@ -12,6 +12,8 @@ To use this library you might need to have the latest git versions of [ESP32](ht
|
||||
|
||||
## Table of contents
|
||||
- [ESPAsyncWebServer ](#espasyncwebserver-)
|
||||
- [Installation](#installation)
|
||||
- [Using PlatformIO](#using-platformio)
|
||||
- [Why should you care](#why-should-you-care)
|
||||
- [Important things to remember](#important-things-to-remember)
|
||||
- [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)
|
||||
- [FILE Upload handling](#file-upload-handling)
|
||||
- [Body data handling](#body-data-handling)
|
||||
- [JSON body handling with ArduinoJson](#json-body-handling-with-arduinojson)
|
||||
- [Responses](#responses)
|
||||
- [Redirect to another URL](#redirect-to-another-url)
|
||||
- [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)
|
||||
- [Methods for controlling websocket connections](#methods-for-controlling-websocket-connections)
|
||||
- [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
|
||||
- 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
|
||||
@ -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
|
||||
### 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"
|
||||
|
||||
|
||||
AsyncResponseStream *response = request->beginResponseStream("text/json");
|
||||
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject &root = jsonBuffer.createObject();
|
||||
root["heap"] = ESP.getFreeHeap();
|
||||
@ -942,7 +986,7 @@ ws.printfAll(arguments...);
|
||||
//printf_P to a client
|
||||
ws.printf_P((uint32_t)client_id, PSTR(format), arguments...);
|
||||
//printfAll_P to all clients
|
||||
ws.printf_P(PSTR(format), arguments...);
|
||||
ws.printfAll_P(PSTR(format), arguments...);
|
||||
//send text to a client
|
||||
ws.text((uint32_t)client_id, (char*)text);
|
||||
ws.text((uint32_t)client_id, (uint8_t*)text, (size_t)len);
|
||||
@ -1097,7 +1141,7 @@ server.on("/scan", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
}
|
||||
}
|
||||
json += "]";
|
||||
request->send(200, "text/json", json);
|
||||
request->send(200, "application/json", json);
|
||||
json = String();
|
||||
});
|
||||
```
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
#include <ESPAsyncTCP.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SPIFFSEditor.h>
|
||||
#include <ESP8266SSDP.h>
|
||||
|
||||
// SKETCH BEGIN
|
||||
AsyncWebServer server(80);
|
||||
AsyncWebSocket ws("/ws");
|
||||
|
@ -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() {
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name":"AsyncWebServer",
|
||||
"description":"Asynchronous HTTP and WebSocket Server Library for ESP32",
|
||||
"name":"ESP Async WebServer",
|
||||
"description":"Asynchronous HTTP and WebSocket Server Library for ESP8266 and ESP32",
|
||||
"keywords":"http,async,websocket,webserver",
|
||||
"authors":
|
||||
{
|
||||
@ -12,12 +12,18 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/me-no-dev/ESPAsyncWebServer.git"
|
||||
},
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"license": "LGPL-3.0",
|
||||
"frameworks": "arduino",
|
||||
"platforms": ["espressif32", "espressif32_stage"],
|
||||
"dependencies":
|
||||
"platforms": ["espressif8266", "espressif32"],
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "AsyncTCP"
|
||||
"name": "ESPAsyncTCP",
|
||||
"platforms": "espressif8266"
|
||||
},
|
||||
{
|
||||
"name": "AsyncTCP",
|
||||
"platforms": "espressif32"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
name=ESP Async WebServer
|
||||
version=1.1.0
|
||||
version=1.2.0
|
||||
author=Me-No-Dev
|
||||
maintainer=Me-No-Dev
|
||||
sentence=Async Web Server for ESP8266 and ESP31B
|
||||
|
@ -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.
|
||||
|
||||
example of callback in use
|
||||
Example of callback in use
|
||||
|
||||
server.on("/json", HTTP_ANY, [](AsyncWebServerRequest * request) {
|
||||
|
||||
@ -17,11 +17,27 @@
|
||||
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_
|
||||
#define ASYNC_JSON_H_
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
constexpr char* JSON_MIMETYPE = "application/json";
|
||||
|
||||
/*
|
||||
* Json Response
|
||||
* */
|
||||
@ -57,7 +73,7 @@ class AsyncJsonResponse: public AsyncAbstractResponse {
|
||||
public:
|
||||
AsyncJsonResponse(bool isArray=false): _isValid{false} {
|
||||
_code = 200;
|
||||
_contentType = "text/json";
|
||||
_contentType = JSON_MIMETYPE;
|
||||
if(isArray)
|
||||
_root = _jsonBuffer.createArray();
|
||||
else
|
||||
@ -80,4 +96,68 @@ class AsyncJsonResponse: public AsyncAbstractResponse {
|
||||
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
|
||||
|
@ -146,7 +146,7 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t * data, size_t
|
||||
return;
|
||||
}
|
||||
|
||||
_data = new uint8_t[size + 1];
|
||||
_data = new uint8_t[_len + 1];
|
||||
|
||||
if (_data) {
|
||||
memcpy(_data, data, _len);
|
||||
@ -224,7 +224,7 @@ bool AsyncWebSocketMessageBuffer::reserve(size_t size)
|
||||
_data = nullptr;
|
||||
}
|
||||
|
||||
_data = new uint8_t[size];
|
||||
_data = new uint8_t[_len + 1];
|
||||
|
||||
if (_data) {
|
||||
_data[_len] = 0;
|
||||
@ -337,14 +337,31 @@ AsyncWebSocketBasicMessage::~AsyncWebSocketBasicMessage() {
|
||||
_status = WS_MSG_SENT;
|
||||
return 0;
|
||||
}
|
||||
size_t window = webSocketSendFrameWindow(client);
|
||||
if(_sent > _len){
|
||||
_status = WS_MSG_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t toSend = _len - _sent;
|
||||
if(window < toSend) toSend = window;
|
||||
bool final = ((toSend + _sent) == _len);
|
||||
size_t sent = webSocketSendFrame(client, final, (_sent == 0)?_opcode:(int)WS_CONTINUATION, _mask, (uint8_t*)(_data+_sent), toSend);
|
||||
_sent += sent;
|
||||
uint8_t headLen = ((sent < 126)?2:4)+(_mask*4);
|
||||
_ack += sent + headLen;
|
||||
size_t window = webSocketSendFrameWindow(client);
|
||||
|
||||
if(window < toSend) {
|
||||
toSend = window;
|
||||
}
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
@ -384,6 +401,7 @@ AsyncWebSocketMultiMessage::AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuff
|
||||
_data = buffer->get();
|
||||
_len = buffer->length();
|
||||
_status = WS_MSG_SENDING;
|
||||
//ets_printf("M: %u\n", _len);
|
||||
} else {
|
||||
_status = WS_MSG_ERROR;
|
||||
}
|
||||
@ -399,9 +417,10 @@ AsyncWebSocketMultiMessage::~AsyncWebSocketMultiMessage() {
|
||||
|
||||
void AsyncWebSocketMultiMessage::ack(size_t len, uint32_t time) {
|
||||
_acked += len;
|
||||
if(_sent == _len && _acked == _ack){
|
||||
if(_sent >= _len && _acked >= _ack){
|
||||
_status = WS_MSG_SENT;
|
||||
}
|
||||
//ets_printf("A: %u\n", len);
|
||||
}
|
||||
size_t AsyncWebSocketMultiMessage::send(AsyncClient *client) {
|
||||
if(_status != WS_MSG_SENDING)
|
||||
@ -410,18 +429,39 @@ AsyncWebSocketMultiMessage::~AsyncWebSocketMultiMessage() {
|
||||
return 0;
|
||||
}
|
||||
if(_sent == _len){
|
||||
if(_acked == _ack)
|
||||
_status = WS_MSG_SENT;
|
||||
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;
|
||||
if(window < toSend) toSend = window;
|
||||
bool final = ((toSend + _sent) == _len);
|
||||
size_t sent = webSocketSendFrame(client, final, (_sent == 0)?_opcode:(int)WS_CONTINUATION, _mask, (uint8_t*)(_data+_sent), toSend);
|
||||
_sent += sent;
|
||||
uint8_t headLen = ((sent < 126)?2:4)+(_mask*4);
|
||||
_ack += sent + headLen;
|
||||
size_t window = webSocketSendFrameWindow(client);
|
||||
|
||||
if(window < toSend) {
|
||||
toSend = window;
|
||||
}
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
@ -511,7 +551,12 @@ void AsyncWebSocketClient::_queueMessage(AsyncWebSocketMessage *dataMessage){
|
||||
delete dataMessage;
|
||||
return;
|
||||
}
|
||||
if(_messageQueue.length() > WS_MAX_QUEUED_MESSAGES){
|
||||
ets_printf("ERROR: Too many messages queued\n");
|
||||
delete dataMessage;
|
||||
} else {
|
||||
_messageQueue.add(dataMessage);
|
||||
}
|
||||
if(_client->canSend())
|
||||
_runQueue();
|
||||
}
|
||||
@ -565,44 +610,45 @@ void AsyncWebSocketClient::_onDisconnect(){
|
||||
_server->_handleDisconnect(this);
|
||||
}
|
||||
|
||||
void AsyncWebSocketClient::_onData(void *buf, size_t plen){
|
||||
void AsyncWebSocketClient::_onData(void *pbuf, size_t plen){
|
||||
_lastMessageTime = millis();
|
||||
uint8_t *fdata = (uint8_t*)buf;
|
||||
uint8_t * data = fdata;
|
||||
uint8_t *data = (uint8_t*)pbuf;
|
||||
while(plen > 0){
|
||||
if(!_pstate){
|
||||
const uint8_t *fdata = data;
|
||||
_pinfo.index = 0;
|
||||
_pinfo.final = (fdata[0] & 0x80) != 0;
|
||||
_pinfo.opcode = fdata[0] & 0x0F;
|
||||
_pinfo.masked = (fdata[1] & 0x80) != 0;
|
||||
_pinfo.len = fdata[1] & 0x7F;
|
||||
data += 2;
|
||||
plen = plen - 2;
|
||||
plen -= 2;
|
||||
if(_pinfo.len == 126){
|
||||
_pinfo.len = fdata[3] | (uint16_t)(fdata[2]) << 8;
|
||||
data += 2;
|
||||
plen = plen - 2;
|
||||
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 = plen - 8;
|
||||
plen -= 8;
|
||||
}
|
||||
|
||||
if(_pinfo.masked){
|
||||
memcpy(_pinfo.mask, data, 4);
|
||||
data += 4;
|
||||
plen = plen - 4;
|
||||
size_t i;
|
||||
for(i=0;i<plen;i++)
|
||||
data[i] = data[i] ^ _pinfo.mask[(_pinfo.index+i)%4];
|
||||
plen -= 4;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
const size_t datalen = std::min((size_t)(_pinfo.len - _pinfo.index), plen);
|
||||
const auto datalast = data[datalen];
|
||||
|
||||
if(_pinfo.masked){
|
||||
size_t i;
|
||||
for(i=0;i<plen;i++)
|
||||
data[i] = data[i] ^ _pinfo.mask[(_pinfo.index+i)%4];
|
||||
for(size_t i=0;i<datalen;i++)
|
||||
data[i] ^= _pinfo.mask[(_pinfo.index+i)%4];
|
||||
}
|
||||
}
|
||||
if((plen + _pinfo.index) < _pinfo.len){
|
||||
|
||||
if((datalen + _pinfo.index) < _pinfo.len){
|
||||
_pstate = 1;
|
||||
|
||||
if(_pinfo.index == 0){
|
||||
@ -611,13 +657,13 @@ void AsyncWebSocketClient::_onData(void *buf, size_t plen){
|
||||
_pinfo.num = 0;
|
||||
} else _pinfo.num += 1;
|
||||
}
|
||||
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, plen);
|
||||
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, datalen);
|
||||
|
||||
_pinfo.index += plen;
|
||||
} else if((plen + _pinfo.index) == _pinfo.len){
|
||||
_pinfo.index += datalen;
|
||||
} else if((datalen + _pinfo.index) == _pinfo.len){
|
||||
_pstate = 0;
|
||||
if(_pinfo.opcode == WS_DISCONNECT){
|
||||
if(plen){
|
||||
if(datalen){
|
||||
uint16_t reasonCode = (uint16_t)(data[0] << 8) + data[1];
|
||||
char * reasonString = (char*)(data+2);
|
||||
if(reasonCode > 1001){
|
||||
@ -629,19 +675,28 @@ void AsyncWebSocketClient::_onData(void *buf, size_t plen){
|
||||
_client->close(true);
|
||||
} else {
|
||||
_status = WS_DISCONNECTING;
|
||||
_queueControl(new AsyncWebSocketControl(WS_DISCONNECT, data, plen));
|
||||
_queueControl(new AsyncWebSocketControl(WS_DISCONNECT, data, datalen));
|
||||
}
|
||||
} else if(_pinfo.opcode == WS_PING){
|
||||
_queueControl(new AsyncWebSocketControl(WS_PONG, data, plen));
|
||||
_queueControl(new AsyncWebSocketControl(WS_PONG, data, datalen));
|
||||
} 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);
|
||||
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, (uint8_t*)data, plen);
|
||||
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, datalen);
|
||||
}
|
||||
} else {
|
||||
//os_printf("frame error: len: %u, index: %llu, total: %llu\n", plen, _pinfo.index, _pinfo.len);
|
||||
//os_printf("frame error: len: %u, index: %llu, total: %llu\n", datalen, _pinfo.index, _pinfo.len);
|
||||
//what should we do?
|
||||
break;
|
||||
}
|
||||
|
||||
// restore byte as _handleEvent may have added a null terminator i.e., data[len] = 0;
|
||||
if (datalen > 0)
|
||||
data[datalen] = datalast;
|
||||
|
||||
data += datalen;
|
||||
plen -= datalen;
|
||||
}
|
||||
}
|
||||
|
||||
@ -873,9 +928,10 @@ void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer){
|
||||
if (!buffer) return;
|
||||
buffer->lock();
|
||||
for(const auto& c: _clients){
|
||||
if(c->status() == WS_CONNECTED)
|
||||
if(c->status() == WS_CONNECTED){
|
||||
c->text(buffer);
|
||||
}
|
||||
}
|
||||
buffer->unlock();
|
||||
_cleanBuffers();
|
||||
}
|
||||
@ -946,7 +1002,7 @@ size_t AsyncWebSocket::printfAll(const char *format, ...) {
|
||||
va_end(arg);
|
||||
delete[] temp;
|
||||
|
||||
AsyncWebSocketMessageBuffer * buffer = makeBuffer(len + 1);
|
||||
AsyncWebSocketMessageBuffer * buffer = makeBuffer(len);
|
||||
if (!buffer) {
|
||||
return 0;
|
||||
}
|
||||
@ -1140,11 +1196,12 @@ AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t
|
||||
|
||||
void AsyncWebSocket::_cleanBuffers()
|
||||
{
|
||||
for(const auto& c: _buffers){
|
||||
if(c->canDelete())
|
||||
for(AsyncWebSocketMessageBuffer * c: _buffers){
|
||||
if(c && c->canDelete()){
|
||||
_buffers.remove(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
@ -24,8 +24,10 @@
|
||||
#include <Arduino.h>
|
||||
#ifdef ESP32
|
||||
#include <AsyncTCP.h>
|
||||
#define WS_MAX_QUEUED_MESSAGES 32
|
||||
#else
|
||||
#include <ESPAsyncTCP.h>
|
||||
#define WS_MAX_QUEUED_MESSAGES 8
|
||||
#endif
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
@ -35,13 +37,26 @@ class AsyncWebSocketClient;
|
||||
class AsyncWebSocketControl;
|
||||
|
||||
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;
|
||||
/** Frame number of a fragmented message. */
|
||||
uint32_t num;
|
||||
/** Is this the last frame in a fragmented message ?*/
|
||||
uint8_t final;
|
||||
/** Is this frame 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;
|
||||
/** Length of the current frame.
|
||||
* This equals the total length of the message if num == 0 && final == true */
|
||||
uint64_t len;
|
||||
/** Mask key */
|
||||
uint8_t mask[4];
|
||||
/** Offset of the data inside the current frame. */
|
||||
uint64_t index;
|
||||
} AwsFrameInfo;
|
||||
|
||||
@ -154,6 +169,8 @@ class AsyncWebSocketClient {
|
||||
uint32_t id(){ return _clientId; }
|
||||
AwsClientStatus status(){ return _status; }
|
||||
AsyncClient* client(){ return _client; }
|
||||
AsyncWebSocket *server(){ return _server; }
|
||||
AwsFrameInfo const &pinfo() const { return _pinfo; }
|
||||
|
||||
IPAddress remoteIP();
|
||||
uint16_t remotePort();
|
||||
@ -199,7 +216,7 @@ class AsyncWebSocketClient {
|
||||
void _onPoll();
|
||||
void _onTimeout(uint32_t time);
|
||||
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;
|
||||
|
@ -51,6 +51,7 @@ class AsyncStaticWebHandler;
|
||||
class AsyncCallbackWebHandler;
|
||||
class AsyncResponseStream;
|
||||
|
||||
#ifndef WEBSERVER_H
|
||||
typedef enum {
|
||||
HTTP_GET = 0b00000001,
|
||||
HTTP_POST = 0b00000010,
|
||||
@ -61,6 +62,11 @@ typedef enum {
|
||||
HTTP_OPTIONS = 0b01000000,
|
||||
HTTP_ANY = 0b01111111,
|
||||
} 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 std::function<void(void)> ArDisconnectHandler;
|
||||
|
||||
@ -130,6 +136,7 @@ class AsyncWebServerRequest {
|
||||
AsyncWebServerResponse* _response;
|
||||
StringArray _interestingHeaders;
|
||||
ArDisconnectHandler _onDisconnectfn;
|
||||
|
||||
String _temp;
|
||||
uint8_t _parseState;
|
||||
|
||||
@ -204,6 +211,7 @@ class AsyncWebServerRequest {
|
||||
RequestedConnectionType requestedConnType() const { return _reqconntype; }
|
||||
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
|
||||
void onDisconnect (ArDisconnectHandler fn);
|
||||
|
||||
//hash is the string representation of:
|
||||
// base64(user:pass) for basic or
|
||||
// user:realm:md5(user:realm:pass) for digest
|
||||
@ -323,6 +331,7 @@ class AsyncWebHandler {
|
||||
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 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;}
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -477,7 +477,7 @@ void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request){
|
||||
dir.close();
|
||||
#endif
|
||||
output += "]";
|
||||
request->send(200, "text/json", output);
|
||||
request->send(200, "application/json", output);
|
||||
output = String();
|
||||
}
|
||||
else if(request->hasParam("edit") || request->hasParam("download")){
|
||||
|
@ -18,6 +18,7 @@ class SPIFFSEditor: public AsyncWebHandler {
|
||||
virtual bool canHandle(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 bool isRequestHandlerTrivial() override final {return false;}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -104,6 +104,7 @@ class AsyncCallbackWebHandler: public AsyncWebHandler {
|
||||
if(_onBody)
|
||||
_onBody(request, data, len, index, total);
|
||||
}
|
||||
virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
|
||||
};
|
||||
|
||||
#endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */
|
||||
|
@ -127,12 +127,18 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
|
||||
}
|
||||
}
|
||||
} 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(needParse){
|
||||
size_t i;
|
||||
for(i=0; i<len; i++){
|
||||
_parseMultipartPostByte(((uint8_t*)buf)[i], i == len - 1);
|
||||
_parsedLength++;
|
||||
}
|
||||
} else
|
||||
_parsedLength += len;
|
||||
} else {
|
||||
if(_parsedLength == 0){
|
||||
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
|
||||
if(_handler) _handler->handleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength);
|
||||
_parsedLength += len;
|
||||
} else {
|
||||
} else if(needParse) {
|
||||
size_t i;
|
||||
for(i=0; i<len; i++){
|
||||
_parsedLength++;
|
||||
_parsePlainPostChar(((uint8_t*)buf)[i]);
|
||||
}
|
||||
} else {
|
||||
_parsedLength += len;
|
||||
}
|
||||
}
|
||||
if(_parsedLength == _contentLength){
|
||||
@ -886,7 +894,7 @@ const String& AsyncWebServerRequest::arg(const __FlashStringHelper * data) const
|
||||
size_t n = strlen_P(p);
|
||||
char * name = (char*) malloc(n+1);
|
||||
if (name) {
|
||||
strcpy(name, p);
|
||||
strcpy_P(name, p);
|
||||
const String & result = arg( String(name) );
|
||||
free(name);
|
||||
return result;
|
||||
|
@ -42,6 +42,10 @@ class AsyncBasicResponse: public AsyncWebServerResponse {
|
||||
class AsyncAbstractResponse: public AsyncWebServerResponse {
|
||||
private:
|
||||
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;
|
||||
size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len);
|
||||
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; }
|
||||
};
|
||||
|
||||
#ifndef TEMPLATE_PLACEHOLDER
|
||||
#define TEMPLATE_PLACEHOLDER '%'
|
||||
#endif
|
||||
|
||||
#define TEMPLATE_PARAM_NAME_LENGTH 32
|
||||
class AsyncFileResponse: public AsyncAbstractResponse {
|
||||
using File = fs::File;
|
||||
|
@ -302,9 +302,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
|
||||
}
|
||||
|
||||
if(headLen){
|
||||
//TODO: memcpy should be faster?
|
||||
sprintf((char*)buf, "%s", _head.c_str());
|
||||
_head = String();
|
||||
memcpy(buf, _head.c_str(), _head.length());
|
||||
}
|
||||
|
||||
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.
|
||||
// See RFC2616 sections 2, 3.6.1.
|
||||
readLen = _fillBufferAndProcessTemplates(buf+headLen+6, outLen - 8);
|
||||
if(readLen == RESPONSE_TRY_AGAIN){
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
outLen = sprintf((char*)buf+headLen, "%x", readLen) + headLen;
|
||||
while(outLen < headLen + 4) buf[outLen++] = ' ';
|
||||
buf[outLen++] = '\r';
|
||||
@ -321,16 +323,27 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
|
||||
buf[outLen++] = '\r';
|
||||
buf[outLen++] = '\n';
|
||||
} 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)
|
||||
_writtenLength += request->client()->write((const char*)buf, outLen);
|
||||
if(headLen){
|
||||
_head = String();
|
||||
}
|
||||
|
||||
if(_chunked)
|
||||
if(outLen){
|
||||
_writtenLength += request->client()->write((const char*)buf, outLen);
|
||||
}
|
||||
|
||||
if(_chunked){
|
||||
_sentLength += readLen;
|
||||
else
|
||||
} else {
|
||||
_sentLength += outLen - headLen;
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
@ -378,8 +391,6 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
|
||||
// temporary buffer to hold parameter name
|
||||
uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1];
|
||||
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(pTemplateEnd) {
|
||||
// prepare argument to callback
|
||||
@ -403,16 +414,14 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
|
||||
// prepare argument to callback
|
||||
*pTemplateEnd = 0;
|
||||
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)
|
||||
const size_t pos = _cache.size();
|
||||
_cache.insert(_cache.end(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
|
||||
i = _cache.begin() + pos;
|
||||
// Copy remaining read-ahead data into cache
|
||||
_cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
|
||||
pTemplateEnd = &data[len - 1];
|
||||
}
|
||||
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
|
||||
_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;
|
||||
}
|
||||
}
|
||||
@ -434,9 +443,7 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
|
||||
// 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
|
||||
if((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) {
|
||||
size_t pos = i - _cache.begin();
|
||||
_cache.insert(i, &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]);
|
||||
i = _cache.begin() + pos;
|
||||
_cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]);
|
||||
//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);
|
||||
} else if(pTemplateEnd + 1 != pTemplateStart + numBytesCopied)
|
||||
@ -447,7 +454,7 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
|
||||
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(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...
|
||||
// there is some free room, fill it from cache
|
||||
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";
|
||||
else if (path.endsWith(".htm")) _contentType = "text/html";
|
||||
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(".png")) _contentType = "image/png";
|
||||
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 ret = _content(data, len, _filledLength);
|
||||
if(ret != RESPONSE_TRY_AGAIN){
|
||||
_filledLength += 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 ret = _content(data, len, _filledLength);
|
||||
if(ret != RESPONSE_TRY_AGAIN){
|
||||
_filledLength += ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -79,6 +79,7 @@ bool AsyncWebServer::removeHandler(AsyncWebHandler *handler){
|
||||
}
|
||||
|
||||
void AsyncWebServer::begin(){
|
||||
_server.setNoDelay(true);
|
||||
_server.begin();
|
||||
}
|
||||
|
||||
|
@ -60,8 +60,9 @@
|
||||
bool can_process_serial = true;
|
||||
|
||||
extern bool deleteRecursive(String path);
|
||||
extern void CloseSerialUpload (bool iserror, String & filename);
|
||||
extern bool sendLine2Serial (String & line);
|
||||
extern bool sendLine2Serial (String & line, int32_t linenb, int32_t * newlinenb);
|
||||
extern void CloseSerialUpload (bool iserror, String & filename , int32_t linenb);
|
||||
extern bool purge_serial();
|
||||
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";
|
||||
@ -1092,6 +1093,7 @@ void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size
|
||||
LOG ("Uploading: ")
|
||||
LOG (filename)
|
||||
LOG ("\n")
|
||||
static int32_t lineNb =-1;
|
||||
static String current_line;
|
||||
static bool is_comment = false;
|
||||
static String current_filename;
|
||||
@ -1107,74 +1109,65 @@ void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size
|
||||
//Upload start
|
||||
//**************
|
||||
if (!index) {
|
||||
LOG ("Starting\n")
|
||||
LOG("Upload Start\r\n")
|
||||
String command = "M29";
|
||||
String resetcmd = "M110 N0";
|
||||
if (CONFIG::GetFirmwareTarget() == SMOOTHIEWARE)resetcmd = "N0 M110";
|
||||
lineNb=1;
|
||||
//close any ongoing upload and get current line number
|
||||
if(!sendLine2Serial (command,1, &lineNb)){
|
||||
//it can failed for repetier
|
||||
if ( ( CONFIG::GetFirmwareTarget() == REPETIER4DV) || (CONFIG::GetFirmwareTarget() == REPETIER) ) {
|
||||
if(!sendLine2Serial (command,-1, NULL)){
|
||||
LOG("Start Upload failed")
|
||||
web_interface->_upload_status= UPLOAD_STATUS_FAILED;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
LOG("Start Upload failed")
|
||||
web_interface->_upload_status= UPLOAD_STATUS_FAILED;
|
||||
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 ="";
|
||||
//init flags
|
||||
is_comment = false;
|
||||
current_filename = filename;
|
||||
web_interface->_upload_status = UPLOAD_STATUS_ONGOING;
|
||||
is_comment = false;
|
||||
String response;
|
||||
ESPCOM::println (F ("Uploading..."), PRINTER_PIPE);
|
||||
//Clear all serial
|
||||
ESPCOM::flush (DEFAULT_PRINTER_PIPE);
|
||||
LOG ("Clear Serial\r\n");
|
||||
if(ESPCOM::available(DEFAULT_PRINTER_PIPE)) {
|
||||
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");
|
||||
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;
|
||||
request->client()->abort();
|
||||
free(sbuf);
|
||||
return;
|
||||
}
|
||||
free(sbuf);
|
||||
}
|
||||
if ( (millis() - timeout) > SERIAL_CHECK_TIMEOUT) {
|
||||
done = true;
|
||||
}
|
||||
LOG("Creation failed\r\n");
|
||||
}
|
||||
}
|
||||
//Upload write
|
||||
@ -1196,10 +1189,10 @@ void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size
|
||||
if (current_line.length() < 126) {
|
||||
//do we have something in buffer ?
|
||||
if (current_line.length() > 0 ) {
|
||||
current_line += "\r\n";
|
||||
if (!sendLine2Serial (current_line) ) {
|
||||
LOG ("Error over buffer\n")
|
||||
CloseSerialUpload (true, current_filename);
|
||||
lineNb++;
|
||||
if (!sendLine2Serial (current_line, lineNb, NULL) ) {
|
||||
LOG ("Error sending line\n")
|
||||
CloseSerialUpload (true, current_filename,lineNb);
|
||||
request->client()->abort();
|
||||
return;
|
||||
}
|
||||
@ -1212,7 +1205,8 @@ void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size
|
||||
} else {
|
||||
//error buffer overload
|
||||
LOG ("Error over buffer\n")
|
||||
CloseSerialUpload (true, current_filename);
|
||||
lineNb++;
|
||||
CloseSerialUpload (true, current_filename, lineNb);
|
||||
request->client()->abort();
|
||||
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
|
||||
} else {
|
||||
LOG ("Error over buffer\n")
|
||||
CloseSerialUpload (true, current_filename);
|
||||
lineNb++;
|
||||
CloseSerialUpload (true, current_filename, lineNb);
|
||||
request->client()->abort();
|
||||
return;
|
||||
}
|
||||
@ -1238,16 +1233,18 @@ void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size
|
||||
LOG ("Final is reached\n")
|
||||
//if last part does not have '\n'
|
||||
if (current_line.length() > 0) {
|
||||
current_line += "\r\n";
|
||||
if (!sendLine2Serial (current_line) ) {
|
||||
lineNb++;
|
||||
if (!sendLine2Serial (current_line, lineNb, NULL) ) {
|
||||
LOG ("Error sending buffer\n")
|
||||
CloseSerialUpload (true, current_filename);
|
||||
lineNb++;
|
||||
CloseSerialUpload (true, current_filename, lineNb);
|
||||
request->client()->abort();
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOG ("Upload finished ");
|
||||
CloseSerialUpload (false, current_filename);
|
||||
lineNb++;
|
||||
CloseSerialUpload (false, current_filename, lineNb);
|
||||
}
|
||||
LOG ("Exit fn\n")
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
*/
|
||||
|
||||
//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"
|
||||
|
||||
//Customize ESP3D ////////////////////////////////////////////////////////////////////////
|
||||
|
Loading…
x
Reference in New Issue
Block a user