mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
update libraries
This commit is contained in:
@@ -1,10 +1,9 @@
|
|||||||
# AsyncTCP
|
# AsyncTCP
|
||||||
[](https://travis-ci.org/me-no-dev/AsyncTCP)  [](https://www.codacy.com/manual/me-no-dev/AsyncTCP?utm_source=github.com&utm_medium=referral&utm_content=me-no-dev/AsyncTCP&utm_campaign=Badge_Grade)
|

|
||||||
|
A fork of the [AsyncTCP](https://github.com/me-no-dev/AsyncTCP) library by [@me-no-dev](https://github.com/me-no-dev) for [ESPHome](https://esphome.io).
|
||||||
|
|
||||||
### Async TCP Library for ESP32 Arduino
|
### Async TCP Library for ESP32 Arduino
|
||||||
|
|
||||||
[](https://gitter.im/me-no-dev/ESPAsyncWebServer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
||||||
|
|
||||||
This is a fully asynchronous TCP library, aimed at enabling trouble-free, multi-connection network environment for Espressif's ESP32 MCUs.
|
This is a fully asynchronous TCP library, aimed at enabling trouble-free, multi-connection network environment for Espressif's ESP32 MCUs.
|
||||||
|
|
||||||
This library is the base for [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer)
|
This library is the base for [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer)
|
||||||
|
|||||||
23
lib/AsyncTCP/library.json
Normal file
23
lib/AsyncTCP/library.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "AsyncTCP-esphome",
|
||||||
|
"description": "Asynchronous TCP Library for ESP32",
|
||||||
|
"keywords": "async,tcp",
|
||||||
|
"authors": {
|
||||||
|
"name": "Hristo Gochkov",
|
||||||
|
"maintainer": true
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/esphome/AsyncTCP.git"
|
||||||
|
},
|
||||||
|
"version": "2.1.1",
|
||||||
|
"license": "LGPL-3.0",
|
||||||
|
"frameworks": "arduino",
|
||||||
|
"platforms": [
|
||||||
|
"espressif32",
|
||||||
|
"libretiny"
|
||||||
|
],
|
||||||
|
"build": {
|
||||||
|
"libCompatMode": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
name=AsyncTCP
|
|
||||||
version=1.1.1
|
|
||||||
author=Me-No-Dev
|
|
||||||
maintainer=Me-No-Dev
|
|
||||||
sentence=Async TCP Library for ESP32
|
|
||||||
paragraph=Async TCP Library for ESP32
|
|
||||||
category=Other
|
|
||||||
url=https://github.com/me-no-dev/AsyncTCP
|
|
||||||
architectures=*
|
|
||||||
@@ -29,22 +29,16 @@ extern "C" {
|
|||||||
#include "lwip/dns.h"
|
#include "lwip/dns.h"
|
||||||
#include "lwip/err.h"
|
#include "lwip/err.h"
|
||||||
}
|
}
|
||||||
|
#if CONFIG_ASYNC_TCP_USE_WDT
|
||||||
#include "esp_task_wdt.h"
|
#include "esp_task_wdt.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TCP/IP Event Task
|
* TCP/IP Event Task
|
||||||
* */
|
* */
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
LWIP_TCP_SENT,
|
LWIP_TCP_SENT, LWIP_TCP_RECV, LWIP_TCP_FIN, LWIP_TCP_ERROR, LWIP_TCP_POLL, LWIP_TCP_CLEAR, LWIP_TCP_ACCEPT, LWIP_TCP_CONNECTED, LWIP_TCP_DNS
|
||||||
LWIP_TCP_RECV,
|
|
||||||
LWIP_TCP_FIN,
|
|
||||||
LWIP_TCP_ERROR,
|
|
||||||
LWIP_TCP_POLL,
|
|
||||||
LWIP_TCP_CLEAR,
|
|
||||||
LWIP_TCP_ACCEPT,
|
|
||||||
LWIP_TCP_CONNECTED,
|
|
||||||
LWIP_TCP_DNS
|
|
||||||
} lwip_event_t;
|
} lwip_event_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -84,7 +78,7 @@ typedef struct {
|
|||||||
};
|
};
|
||||||
} lwip_event_packet_t;
|
} lwip_event_packet_t;
|
||||||
|
|
||||||
static QueueHandle_t _async_queue;
|
static xQueueHandle _async_queue;
|
||||||
static TaskHandle_t _async_service_task_handle = NULL;
|
static TaskHandle_t _async_service_task_handle = NULL;
|
||||||
|
|
||||||
|
|
||||||
@@ -103,7 +97,7 @@ static uint32_t _closed_index = []() {
|
|||||||
|
|
||||||
static inline bool _init_async_event_queue(){
|
static inline bool _init_async_event_queue(){
|
||||||
if(!_async_queue){
|
if(!_async_queue){
|
||||||
_async_queue = xQueueCreate(CONFIG_ASYNC_TCP_QUEUE, sizeof(lwip_event_packet_t *)); // double queue to 128 see https://github.com/emsesp/EMS-ESP32/issues/177
|
_async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t *));
|
||||||
if(!_async_queue){
|
if(!_async_queue){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -221,13 +215,32 @@ static void _stop_async_task(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static bool customTaskCreateUniversal(
|
||||||
|
TaskFunction_t pxTaskCode,
|
||||||
|
const char * const pcName,
|
||||||
|
const uint32_t usStackDepth,
|
||||||
|
void * const pvParameters,
|
||||||
|
UBaseType_t uxPriority,
|
||||||
|
TaskHandle_t * const pxCreatedTask,
|
||||||
|
const BaseType_t xCoreID) {
|
||||||
|
#ifndef CONFIG_FREERTOS_UNICORE
|
||||||
|
if(xCoreID >= 0 && xCoreID < 2) {
|
||||||
|
return xTaskCreatePinnedToCore(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask, xCoreID);
|
||||||
|
} else {
|
||||||
|
#endif
|
||||||
|
return xTaskCreate(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask);
|
||||||
|
#ifndef CONFIG_FREERTOS_UNICORE
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static bool _start_async_task(){
|
static bool _start_async_task(){
|
||||||
if(!_init_async_event_queue()){
|
if(!_init_async_event_queue()){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(!_async_service_task_handle){
|
if(!_async_service_task_handle){
|
||||||
// xTaskCreateUniversal(_async_service_task, "async_tcp", 8192 * 2, NULL, 3, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE);
|
customTaskCreateUniversal(_async_service_task, "async_tcp", CONFIG_ASYNC_TCP_STACK_SIZE, NULL, 3, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE);
|
||||||
xTaskCreate(_async_service_task, "async_tcp", CONFIG_ASYNC_TCP_STACK, NULL, CONFIG_ASYNC_TCP_TASK_PRIORITY, &_async_service_task_handle);
|
|
||||||
if(!_async_service_task_handle){
|
if(!_async_service_task_handle){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -564,15 +577,15 @@ AsyncClient::AsyncClient(tcp_pcb * pcb)
|
|||||||
, _pb_cb_arg(0)
|
, _pb_cb_arg(0)
|
||||||
, _timeout_cb(0)
|
, _timeout_cb(0)
|
||||||
, _timeout_cb_arg(0)
|
, _timeout_cb_arg(0)
|
||||||
, _pcb_busy(false)
|
|
||||||
, _pcb_sent_at(0)
|
|
||||||
, _ack_pcb(true)
|
, _ack_pcb(true)
|
||||||
, _rx_last_packet(0)
|
, _tx_last_packet(0)
|
||||||
, _rx_since_timeout(0)
|
, _rx_timeout(0)
|
||||||
|
, _rx_last_ack(0)
|
||||||
, _ack_timeout(ASYNC_MAX_ACK_TIME)
|
, _ack_timeout(ASYNC_MAX_ACK_TIME)
|
||||||
, _connect_port(0)
|
, _connect_port(0)
|
||||||
, prev(NULL)
|
, prev(NULL)
|
||||||
, next(NULL) {
|
, next(NULL)
|
||||||
|
{
|
||||||
_pcb = pcb;
|
_pcb = pcb;
|
||||||
_closed_slot = -1;
|
_closed_slot = -1;
|
||||||
if(_pcb){
|
if(_pcb){
|
||||||
@@ -736,7 +749,11 @@ bool AsyncClient::connect(const char * host, uint16_t port) {
|
|||||||
if(addr.type == IPADDR_TYPE_V6) {
|
if(addr.type == IPADDR_TYPE_V6) {
|
||||||
return connect(IPv6Address(addr.u_addr.ip6.addr), port);
|
return connect(IPv6Address(addr.u_addr.ip6.addr), port);
|
||||||
}
|
}
|
||||||
|
#if LWIP_IPV6
|
||||||
return connect(IPAddress(addr.u_addr.ip4.addr), port);
|
return connect(IPAddress(addr.u_addr.ip4.addr), port);
|
||||||
|
#else
|
||||||
|
return connect(IPAddress(addr.addr), port);
|
||||||
|
#endif
|
||||||
} else if(err == ERR_INPROGRESS) {
|
} else if(err == ERR_INPROGRESS) {
|
||||||
_connect_port = port;
|
_connect_port = port;
|
||||||
return true;
|
return true;
|
||||||
@@ -785,13 +802,12 @@ size_t AsyncClient::add(const char * data, size_t size, uint8_t apiflags) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncClient::send(){
|
bool AsyncClient::send(){
|
||||||
int8_t err = ERR_OK;
|
auto backup = _tx_last_packet;
|
||||||
err = _tcp_output(_pcb, _closed_slot);
|
_tx_last_packet = millis();
|
||||||
if (err == ERR_OK) {
|
if (_tcp_output(_pcb, _closed_slot) == ERR_OK) {
|
||||||
_pcb_busy = true;
|
|
||||||
_pcb_sent_at = millis();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
_tx_last_packet = backup;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -871,7 +887,6 @@ int8_t AsyncClient::_connected(void * pcb, int8_t err) {
|
|||||||
_pcb = reinterpret_cast<tcp_pcb*>(pcb);
|
_pcb = reinterpret_cast<tcp_pcb*>(pcb);
|
||||||
if(_pcb){
|
if(_pcb){
|
||||||
_rx_last_packet = millis();
|
_rx_last_packet = millis();
|
||||||
_pcb_busy = false;
|
|
||||||
// tcp_recv(_pcb, &_tcp_recv);
|
// tcp_recv(_pcb, &_tcp_recv);
|
||||||
// tcp_sent(_pcb, &_tcp_sent);
|
// tcp_sent(_pcb, &_tcp_sent);
|
||||||
// tcp_poll(_pcb, &_tcp_poll, 1);
|
// tcp_poll(_pcb, &_tcp_poll, 1);
|
||||||
@@ -933,10 +948,10 @@ int8_t AsyncClient::_fin(tcp_pcb * pcb, int8_t err) {
|
|||||||
|
|
||||||
int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) {
|
int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) {
|
||||||
_rx_last_packet = millis();
|
_rx_last_packet = millis();
|
||||||
|
_rx_last_ack = millis();
|
||||||
//log_i("%u", len);
|
//log_i("%u", len);
|
||||||
_pcb_busy = false;
|
|
||||||
if(_sent_cb) {
|
if(_sent_cb) {
|
||||||
_sent_cb(_sent_cb_arg, this, len, (millis() - _pcb_sent_at));
|
_sent_cb(_sent_cb_arg, this, len, (millis() - _tx_last_packet));
|
||||||
}
|
}
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
@@ -979,15 +994,18 @@ int8_t AsyncClient::_poll(tcp_pcb * pcb) {
|
|||||||
uint32_t now = millis();
|
uint32_t now = millis();
|
||||||
|
|
||||||
// ACK Timeout
|
// ACK Timeout
|
||||||
if (_pcb_busy && _ack_timeout && (now - _pcb_sent_at) >= _ack_timeout) {
|
if(_ack_timeout){
|
||||||
_pcb_busy = false;
|
const uint32_t one_day = 86400000;
|
||||||
|
bool last_tx_is_after_last_ack = (_rx_last_ack - _tx_last_packet + one_day) < one_day;
|
||||||
|
if(last_tx_is_after_last_ack && (now - _tx_last_packet) >= _ack_timeout) {
|
||||||
log_w("ack timeout %d", pcb->state);
|
log_w("ack timeout %d", pcb->state);
|
||||||
if(_timeout_cb)
|
if(_timeout_cb)
|
||||||
_timeout_cb(_timeout_cb_arg, this, (now - _pcb_sent_at));
|
_timeout_cb(_timeout_cb_arg, this, (now - _tx_last_packet));
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// RX Timeout
|
// RX Timeout
|
||||||
if (_rx_since_timeout && (now - _rx_last_packet) >= (_rx_since_timeout * 1000)) {
|
if(_rx_timeout && (now - _rx_last_packet) >= (_rx_timeout * 1000)) {
|
||||||
log_w("rx timeout %d", pcb->state);
|
log_w("rx timeout %d", pcb->state);
|
||||||
_close();
|
_close();
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
@@ -1041,21 +1059,18 @@ size_t AsyncClient::write(const char * data) {
|
|||||||
|
|
||||||
size_t AsyncClient::write(const char* data, size_t size, uint8_t apiflags) {
|
size_t AsyncClient::write(const char* data, size_t size, uint8_t apiflags) {
|
||||||
size_t will_send = add(data, size, apiflags);
|
size_t will_send = add(data, size, apiflags);
|
||||||
if (!will_send) {
|
if(!will_send || !send()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
while (connected() && !send()) {
|
|
||||||
taskYIELD();
|
|
||||||
}
|
|
||||||
return will_send;
|
return will_send;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncClient::setRxTimeout(uint32_t timeout){
|
void AsyncClient::setRxTimeout(uint32_t timeout){
|
||||||
_rx_since_timeout = timeout;
|
_rx_timeout = timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AsyncClient::getRxTimeout(){
|
uint32_t AsyncClient::getRxTimeout(){
|
||||||
return _rx_since_timeout;
|
return _rx_timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AsyncClient::getAckTimeout(){
|
uint32_t AsyncClient::getAckTimeout(){
|
||||||
@@ -1084,18 +1099,6 @@ bool AsyncClient::getNoDelay() {
|
|||||||
return tcp_nagle_disabled(_pcb);
|
return tcp_nagle_disabled(_pcb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncClient::setKeepAlive(uint32_t ms, uint8_t cnt) {
|
|
||||||
if (ms != 0) {
|
|
||||||
_pcb->so_options |= SOF_KEEPALIVE; //Turn on TCP Keepalive for the given pcb
|
|
||||||
// Set the time between keepalive messages in milli-seconds
|
|
||||||
_pcb->keep_idle = ms;
|
|
||||||
_pcb->keep_intvl = ms;
|
|
||||||
_pcb->keep_cnt = cnt; //The number of unanswered probes required to force closure of the socket
|
|
||||||
} else {
|
|
||||||
_pcb->so_options &= ~SOF_KEEPALIVE; //Turn off TCP Keepalive for the given pcb
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t AsyncClient::getMss(){
|
uint16_t AsyncClient::getMss(){
|
||||||
if(!_pcb) {
|
if(!_pcb) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1107,7 +1110,11 @@ uint32_t AsyncClient::getRemoteAddress() {
|
|||||||
if(!_pcb) {
|
if(!_pcb) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#if LWIP_IPV4 && LWIP_IPV6
|
||||||
return _pcb->remote_ip.u_addr.ip4.addr;
|
return _pcb->remote_ip.u_addr.ip4.addr;
|
||||||
|
#else
|
||||||
|
return _pcb->remote_ip.addr;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
ip6_addr_t AsyncClient::getRemoteAddress6() {
|
ip6_addr_t AsyncClient::getRemoteAddress6() {
|
||||||
@@ -1130,7 +1137,11 @@ uint32_t AsyncClient::getLocalAddress() {
|
|||||||
if(!_pcb) {
|
if(!_pcb) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#if LWIP_IPV4 && LWIP_IPV6
|
||||||
return _pcb->local_ip.u_addr.ip4.addr;
|
return _pcb->local_ip.u_addr.ip4.addr;
|
||||||
|
#else
|
||||||
|
return _pcb->local_ip.addr;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
ip6_addr_t AsyncClient::getLocalAddress6() {
|
ip6_addr_t AsyncClient::getLocalAddress6() {
|
||||||
@@ -1221,71 +1232,41 @@ bool AsyncClient::canSend() {
|
|||||||
|
|
||||||
const char * AsyncClient::errorToString(int8_t error){
|
const char * AsyncClient::errorToString(int8_t error){
|
||||||
switch(error){
|
switch(error){
|
||||||
case ERR_OK:
|
case ERR_OK: return "OK";
|
||||||
return "OK";
|
case ERR_MEM: return "Out of memory error";
|
||||||
case ERR_MEM:
|
case ERR_BUF: return "Buffer error";
|
||||||
return "Out of memory error";
|
case ERR_TIMEOUT: return "Timeout";
|
||||||
case ERR_BUF:
|
case ERR_RTE: return "Routing problem";
|
||||||
return "Buffer error";
|
case ERR_INPROGRESS: return "Operation in progress";
|
||||||
case ERR_TIMEOUT:
|
case ERR_VAL: return "Illegal value";
|
||||||
return "Timeout";
|
case ERR_WOULDBLOCK: return "Operation would block";
|
||||||
case ERR_RTE:
|
case ERR_USE: return "Address in use";
|
||||||
return "Routing problem";
|
case ERR_ALREADY: return "Already connected";
|
||||||
case ERR_INPROGRESS:
|
case ERR_CONN: return "Not connected";
|
||||||
return "Operation in progress";
|
case ERR_IF: return "Low-level netif error";
|
||||||
case ERR_VAL:
|
case ERR_ABRT: return "Connection aborted";
|
||||||
return "Illegal value";
|
case ERR_RST: return "Connection reset";
|
||||||
case ERR_WOULDBLOCK:
|
case ERR_CLSD: return "Connection closed";
|
||||||
return "Operation would block";
|
case ERR_ARG: return "Illegal argument";
|
||||||
case ERR_USE:
|
case -55: return "DNS failed";
|
||||||
return "Address in use";
|
default: return "UNKNOWN";
|
||||||
case ERR_ALREADY:
|
|
||||||
return "Already connected";
|
|
||||||
case ERR_CONN:
|
|
||||||
return "Not connected";
|
|
||||||
case ERR_IF:
|
|
||||||
return "Low-level netif error";
|
|
||||||
case ERR_ABRT:
|
|
||||||
return "Connection aborted";
|
|
||||||
case ERR_RST:
|
|
||||||
return "Connection reset";
|
|
||||||
case ERR_CLSD:
|
|
||||||
return "Connection closed";
|
|
||||||
case ERR_ARG:
|
|
||||||
return "Illegal argument";
|
|
||||||
case -55:
|
|
||||||
return "DNS failed";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * AsyncClient::stateToString(){
|
const char * AsyncClient::stateToString(){
|
||||||
switch(state()){
|
switch(state()){
|
||||||
case 0:
|
case 0: return "Closed";
|
||||||
return "Closed";
|
case 1: return "Listen";
|
||||||
case 1:
|
case 2: return "SYN Sent";
|
||||||
return "Listen";
|
case 3: return "SYN Received";
|
||||||
case 2:
|
case 4: return "Established";
|
||||||
return "SYN Sent";
|
case 5: return "FIN Wait 1";
|
||||||
case 3:
|
case 6: return "FIN Wait 2";
|
||||||
return "SYN Received";
|
case 7: return "Close Wait";
|
||||||
case 4:
|
case 8: return "Closing";
|
||||||
return "Established";
|
case 9: return "Last ACK";
|
||||||
case 5:
|
case 10: return "Time Wait";
|
||||||
return "FIN Wait 1";
|
default: return "UNKNOWN";
|
||||||
case 6:
|
|
||||||
return "FIN Wait 2";
|
|
||||||
case 7:
|
|
||||||
return "Close Wait";
|
|
||||||
case 8:
|
|
||||||
return "Closing";
|
|
||||||
case 9:
|
|
||||||
return "Last ACK";
|
|
||||||
case 10:
|
|
||||||
return "Time Wait";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1336,8 +1317,8 @@ AsyncServer::AsyncServer(IPAddress addr, uint16_t port)
|
|||||||
, _noDelay(false)
|
, _noDelay(false)
|
||||||
, _pcb(0)
|
, _pcb(0)
|
||||||
, _connect_cb(0)
|
, _connect_cb(0)
|
||||||
, _connect_cb_arg(0) {
|
, _connect_cb_arg(0)
|
||||||
}
|
{}
|
||||||
|
|
||||||
AsyncServer::AsyncServer(IPv6Address addr, uint16_t port)
|
AsyncServer::AsyncServer(IPv6Address addr, uint16_t port)
|
||||||
: _port(port)
|
: _port(port)
|
||||||
@@ -1346,8 +1327,8 @@ AsyncServer::AsyncServer(IPv6Address addr, uint16_t port)
|
|||||||
, _noDelay(false)
|
, _noDelay(false)
|
||||||
, _pcb(0)
|
, _pcb(0)
|
||||||
, _connect_cb(0)
|
, _connect_cb(0)
|
||||||
, _connect_cb_arg(0) {
|
, _connect_cb_arg(0)
|
||||||
}
|
{}
|
||||||
|
|
||||||
AsyncServer::AsyncServer(uint16_t port)
|
AsyncServer::AsyncServer(uint16_t port)
|
||||||
: _port(port)
|
: _port(port)
|
||||||
@@ -1358,8 +1339,8 @@ AsyncServer::AsyncServer(uint16_t port)
|
|||||||
, _noDelay(false)
|
, _noDelay(false)
|
||||||
, _pcb(0)
|
, _pcb(0)
|
||||||
, _connect_cb(0)
|
, _connect_cb(0)
|
||||||
, _connect_cb_arg(0) {
|
, _connect_cb_arg(0)
|
||||||
}
|
{}
|
||||||
|
|
||||||
AsyncServer::~AsyncServer(){
|
AsyncServer::~AsyncServer(){
|
||||||
end();
|
end();
|
||||||
|
|||||||
@@ -24,32 +24,33 @@
|
|||||||
|
|
||||||
#include "IPAddress.h"
|
#include "IPAddress.h"
|
||||||
#include "IPv6Address.h"
|
#include "IPv6Address.h"
|
||||||
#include "sdkconfig.h"
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
|
#ifndef LIBRETINY
|
||||||
|
#include "sdkconfig.h"
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
#include "lwip/pbuf.h"
|
#include "lwip/pbuf.h"
|
||||||
#include "lwip/ip_addr.h"
|
#include "lwip/ip_addr.h"
|
||||||
#include "lwip/ip6_addr.h"
|
#include "lwip/ip6_addr.h"
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
extern "C" {
|
||||||
|
#include <semphr.h>
|
||||||
|
#include <lwip/pbuf.h>
|
||||||
|
}
|
||||||
|
#define CONFIG_ASYNC_TCP_RUNNING_CORE -1 //any available core
|
||||||
|
#define CONFIG_ASYNC_TCP_USE_WDT 0
|
||||||
|
#endif
|
||||||
|
|
||||||
//If core is not defined, then we are running in Arduino or PIO
|
//If core is not defined, then we are running in Arduino or PIO
|
||||||
#ifndef CONFIG_ASYNC_TCP_RUNNING_CORE
|
#ifndef CONFIG_ASYNC_TCP_RUNNING_CORE
|
||||||
#define CONFIG_ASYNC_TCP_RUNNING_CORE -1 //any available core
|
#define CONFIG_ASYNC_TCP_RUNNING_CORE -1 //any available core
|
||||||
#define CONFIG_ASYNC_TCP_USE_WDT 0 //if enabled, adds between 33us and 200us per event
|
#define CONFIG_ASYNC_TCP_USE_WDT 1 //if enabled, adds between 33us and 200us per event
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CONFIG_ASYNC_TCP_TASK_PRIORITY
|
#ifndef CONFIG_ASYNC_TCP_STACK_SIZE
|
||||||
#define CONFIG_ASYNC_TCP_TASK_PRIORITY 5
|
#define CONFIG_ASYNC_TCP_STACK_SIZE 8192 * 2
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef CONFIG_ASYNC_TCP_STACK
|
|
||||||
#define CONFIG_ASYNC_TCP_STACK 8192
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef CONFIG_ASYNC_TCP_QUEUE
|
|
||||||
#define CONFIG_ASYNC_TCP_QUEUE 128
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class AsyncClient;
|
class AsyncClient;
|
||||||
@@ -116,8 +117,6 @@ class AsyncClient {
|
|||||||
void setNoDelay(bool nodelay);
|
void setNoDelay(bool nodelay);
|
||||||
bool getNoDelay();
|
bool getNoDelay();
|
||||||
|
|
||||||
void setKeepAlive(uint32_t ms, uint8_t cnt);
|
|
||||||
|
|
||||||
uint32_t getRemoteAddress();
|
uint32_t getRemoteAddress();
|
||||||
ip6_addr_t getRemoteAddress6();
|
ip6_addr_t getRemoteAddress6();
|
||||||
uint16_t getRemotePort();
|
uint16_t getRemotePort();
|
||||||
@@ -144,9 +143,7 @@ class AsyncClient {
|
|||||||
|
|
||||||
void ackPacket(struct pbuf * pb);//ack pbuf from onPacket
|
void ackPacket(struct pbuf * pb);//ack pbuf from onPacket
|
||||||
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() {
|
void ackLater(){ _ack_pcb = false; } //will not ack the current packet. Call from onData
|
||||||
_ack_pcb = false;
|
|
||||||
} //will not ack the current packet. Call from onData
|
|
||||||
|
|
||||||
const char * errorToString(int8_t error);
|
const char * errorToString(int8_t error);
|
||||||
const char * stateToString();
|
const char * stateToString();
|
||||||
@@ -162,9 +159,7 @@ class AsyncClient {
|
|||||||
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);
|
||||||
|
|
||||||
int8_t _recv(tcp_pcb* pcb, pbuf* pb, int8_t err);
|
int8_t _recv(tcp_pcb* pcb, pbuf* pb, int8_t err);
|
||||||
tcp_pcb * pcb() {
|
tcp_pcb * pcb(){ return _pcb; }
|
||||||
return _pcb;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool _connect(ip_addr_t addr, uint16_t port);
|
bool _connect(ip_addr_t addr, uint16_t port);
|
||||||
@@ -189,12 +184,12 @@ class AsyncClient {
|
|||||||
AcConnectHandler _poll_cb;
|
AcConnectHandler _poll_cb;
|
||||||
void* _poll_cb_arg;
|
void* _poll_cb_arg;
|
||||||
|
|
||||||
bool _pcb_busy;
|
|
||||||
uint32_t _pcb_sent_at;
|
|
||||||
bool _ack_pcb;
|
bool _ack_pcb;
|
||||||
|
uint32_t _tx_last_packet;
|
||||||
uint32_t _rx_ack_len;
|
uint32_t _rx_ack_len;
|
||||||
uint32_t _rx_last_packet;
|
uint32_t _rx_last_packet;
|
||||||
uint32_t _rx_since_timeout;
|
uint32_t _rx_timeout;
|
||||||
|
uint32_t _rx_last_ack;
|
||||||
uint32_t _ack_timeout;
|
uint32_t _ack_timeout;
|
||||||
uint16_t _connect_port;
|
uint16_t _connect_port;
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,87 +0,0 @@
|
|||||||
#ifndef ASYNCWEBSYNCHRONIZATION_H_
|
|
||||||
#define ASYNCWEBSYNCHRONIZATION_H_
|
|
||||||
|
|
||||||
// Synchronisation is only available on ESP32, as the ESP8266 isn't using FreeRTOS by default
|
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
|
|
||||||
#ifdef ESP32
|
|
||||||
|
|
||||||
// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore
|
|
||||||
class AsyncWebLock
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
SemaphoreHandle_t _lock;
|
|
||||||
mutable void *_lockedBy;
|
|
||||||
|
|
||||||
public:
|
|
||||||
AsyncWebLock() {
|
|
||||||
_lock = xSemaphoreCreateBinary();
|
|
||||||
_lockedBy = NULL;
|
|
||||||
xSemaphoreGive(_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
~AsyncWebLock() {
|
|
||||||
vSemaphoreDelete(_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool lock() const {
|
|
||||||
extern void *pxCurrentTCB;
|
|
||||||
if (_lockedBy != pxCurrentTCB) {
|
|
||||||
xSemaphoreTake(_lock, portMAX_DELAY);
|
|
||||||
_lockedBy = pxCurrentTCB;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void unlock() const {
|
|
||||||
_lockedBy = NULL;
|
|
||||||
xSemaphoreGive(_lock);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
// This is the 8266 version of the Sync Lock which is currently unimplemented
|
|
||||||
class AsyncWebLock
|
|
||||||
{
|
|
||||||
|
|
||||||
public:
|
|
||||||
AsyncWebLock() {
|
|
||||||
}
|
|
||||||
|
|
||||||
~AsyncWebLock() {
|
|
||||||
}
|
|
||||||
|
|
||||||
bool lock() const {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void unlock() const {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class AsyncWebLockGuard
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
const AsyncWebLock *_lock;
|
|
||||||
|
|
||||||
public:
|
|
||||||
AsyncWebLockGuard(const AsyncWebLock &l) {
|
|
||||||
if (l.lock()) {
|
|
||||||
_lock = &l;
|
|
||||||
} else {
|
|
||||||
_lock = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~AsyncWebLockGuard() {
|
|
||||||
if (_lock) {
|
|
||||||
_lock->unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // ASYNCWEBSYNCHRONIZATION_H_
|
|
||||||
165
lib/ESPAsyncWebServer/LICENSE
Normal file
165
lib/ESPAsyncWebServer/LICENSE
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
This version of the GNU Lesser General Public License incorporates
|
||||||
|
the terms and conditions of version 3 of the GNU General Public
|
||||||
|
License, supplemented by the additional permissions listed below.
|
||||||
|
|
||||||
|
0. Additional Definitions.
|
||||||
|
|
||||||
|
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||||
|
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||||
|
General Public License.
|
||||||
|
|
||||||
|
"The Library" refers to a covered work governed by this License,
|
||||||
|
other than an Application or a Combined Work as defined below.
|
||||||
|
|
||||||
|
An "Application" is any work that makes use of an interface provided
|
||||||
|
by the Library, but which is not otherwise based on the Library.
|
||||||
|
Defining a subclass of a class defined by the Library is deemed a mode
|
||||||
|
of using an interface provided by the Library.
|
||||||
|
|
||||||
|
A "Combined Work" is a work produced by combining or linking an
|
||||||
|
Application with the Library. The particular version of the Library
|
||||||
|
with which the Combined Work was made is also called the "Linked
|
||||||
|
Version".
|
||||||
|
|
||||||
|
The "Minimal Corresponding Source" for a Combined Work means the
|
||||||
|
Corresponding Source for the Combined Work, excluding any source code
|
||||||
|
for portions of the Combined Work that, considered in isolation, are
|
||||||
|
based on the Application, and not on the Linked Version.
|
||||||
|
|
||||||
|
The "Corresponding Application Code" for a Combined Work means the
|
||||||
|
object code and/or source code for the Application, including any data
|
||||||
|
and utility programs needed for reproducing the Combined Work from the
|
||||||
|
Application, but excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
|
1. Exception to Section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
You may convey a covered work under sections 3 and 4 of this License
|
||||||
|
without being bound by section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
2. Conveying Modified Versions.
|
||||||
|
|
||||||
|
If you modify a copy of the Library, and, in your modifications, a
|
||||||
|
facility refers to a function or data to be supplied by an Application
|
||||||
|
that uses the facility (other than as an argument passed when the
|
||||||
|
facility is invoked), then you may convey a copy of the modified
|
||||||
|
version:
|
||||||
|
|
||||||
|
a) under this License, provided that you make a good faith effort to
|
||||||
|
ensure that, in the event an Application does not supply the
|
||||||
|
function or data, the facility still operates, and performs
|
||||||
|
whatever part of its purpose remains meaningful, or
|
||||||
|
|
||||||
|
b) under the GNU GPL, with none of the additional permissions of
|
||||||
|
this License applicable to that copy.
|
||||||
|
|
||||||
|
3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
|
||||||
|
The object code form of an Application may incorporate material from
|
||||||
|
a header file that is part of the Library. You may convey such object
|
||||||
|
code under terms of your choice, provided that, if the incorporated
|
||||||
|
material is not limited to numerical parameters, data structure
|
||||||
|
layouts and accessors, or small macros, inline functions and templates
|
||||||
|
(ten or fewer lines in length), you do both of the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the object code that the
|
||||||
|
Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
4. Combined Works.
|
||||||
|
|
||||||
|
You may convey a Combined Work under terms of your choice that,
|
||||||
|
taken together, effectively do not restrict modification of the
|
||||||
|
portions of the Library contained in the Combined Work and reverse
|
||||||
|
engineering for debugging such modifications, if you also do each of
|
||||||
|
the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the Combined Work that
|
||||||
|
the Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
c) For a Combined Work that displays copyright notices during
|
||||||
|
execution, include the copyright notice for the Library among
|
||||||
|
these notices, as well as a reference directing the user to the
|
||||||
|
copies of the GNU GPL and this license document.
|
||||||
|
|
||||||
|
d) Do one of the following:
|
||||||
|
|
||||||
|
0) Convey the Minimal Corresponding Source under the terms of this
|
||||||
|
License, and the Corresponding Application Code in a form
|
||||||
|
suitable for, and under terms that permit, the user to
|
||||||
|
recombine or relink the Application with a modified version of
|
||||||
|
the Linked Version to produce a modified Combined Work, in the
|
||||||
|
manner specified by section 6 of the GNU GPL for conveying
|
||||||
|
Corresponding Source.
|
||||||
|
|
||||||
|
1) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (a) uses at run time
|
||||||
|
a copy of the Library already present on the user's computer
|
||||||
|
system, and (b) will operate properly with a modified version
|
||||||
|
of the Library that is interface-compatible with the Linked
|
||||||
|
Version.
|
||||||
|
|
||||||
|
e) Provide Installation Information, but only if you would otherwise
|
||||||
|
be required to provide such information under section 6 of the
|
||||||
|
GNU GPL, and only to the extent that such information is
|
||||||
|
necessary to install and execute a modified version of the
|
||||||
|
Combined Work produced by recombining or relinking the
|
||||||
|
Application with a modified version of the Linked Version. (If
|
||||||
|
you use option 4d0, the Installation Information must accompany
|
||||||
|
the Minimal Corresponding Source and Corresponding Application
|
||||||
|
Code. If you use option 4d1, you must provide the Installation
|
||||||
|
Information in the manner specified by section 6 of the GNU GPL
|
||||||
|
for conveying Corresponding Source.)
|
||||||
|
|
||||||
|
5. Combined Libraries.
|
||||||
|
|
||||||
|
You may place library facilities that are a work based on the
|
||||||
|
Library side by side in a single library together with other library
|
||||||
|
facilities that are not Applications and are not covered by this
|
||||||
|
License, and convey such a combined library under terms of your
|
||||||
|
choice, if you do both of the following:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based
|
||||||
|
on the Library, uncombined with any other library facilities,
|
||||||
|
conveyed under the terms of this License.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library that part of it
|
||||||
|
is a work based on the Library, and explaining where to find the
|
||||||
|
accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
6. Revised Versions of the GNU Lesser General Public License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the GNU Lesser General Public License from time to time. Such new
|
||||||
|
versions will be similar in spirit to the present version, but may
|
||||||
|
differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Library as you received it specifies that a certain numbered version
|
||||||
|
of the GNU Lesser General Public License "or any later version"
|
||||||
|
applies to it, you have the option of following the terms and
|
||||||
|
conditions either of that published version or of any later version
|
||||||
|
published by the Free Software Foundation. If the Library as you
|
||||||
|
received it does not specify a version number of the GNU Lesser
|
||||||
|
General Public License, you may choose any version of the GNU Lesser
|
||||||
|
General Public License ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Library as you received it specifies that a proxy can decide
|
||||||
|
whether future versions of the GNU Lesser General Public License shall
|
||||||
|
apply, that proxy's public statement of acceptance of any version is
|
||||||
|
permanent authorization for you to choose that version for the
|
||||||
|
Library.
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,544 +0,0 @@
|
|||||||
#include "SPIFFSEditor.h"
|
|
||||||
#include <FS.h>
|
|
||||||
|
|
||||||
//File: edit.htm.gz, Size: 4151
|
|
||||||
#define edit_htm_gz_len 4151
|
|
||||||
const uint8_t edit_htm_gz[] PROGMEM = {
|
|
||||||
0x1F, 0x8B, 0x08, 0x08, 0xB8, 0x94, 0xB1, 0x59, 0x00, 0x03, 0x65, 0x64, 0x69, 0x74, 0x2E, 0x68,
|
|
||||||
0x74, 0x6D, 0x00, 0xB5, 0x3A, 0x0B, 0x7B, 0xDA, 0xB8, 0xB2, 0x7F, 0xC5, 0x71, 0xCF, 0x66, 0xED,
|
|
||||||
0x83, 0x31, 0x90, 0xA4, 0xD9, 0xD6, 0xC4, 0xC9, 0x42, 0x92, 0x36, 0x6D, 0xF3, 0x6A, 0x80, 0xB6,
|
|
||||||
0x69, 0x4F, 0xEE, 0x7E, 0xC2, 0x16, 0xA0, 0xC6, 0x96, 0x5D, 0x5B, 0x0E, 0x49, 0x59, 0xFE, 0xFB,
|
|
||||||
0x9D, 0x91, 0x6C, 0xB0, 0x09, 0x69, 0x77, 0xCF, 0xBD, 0xBB, 0xDD, 0x2D, 0x92, 0x46, 0x33, 0x9A,
|
|
||||||
0x19, 0xCD, 0x53, 0xDE, 0xBD, 0x8D, 0xA3, 0x8B, 0xC3, 0xFE, 0xF5, 0xE5, 0xB1, 0x36, 0x11, 0x61,
|
|
||||||
0xB0, 0xBF, 0x87, 0x7F, 0x6B, 0x01, 0xE1, 0x63, 0x97, 0xF2, 0xFD, 0x3D, 0xC1, 0x44, 0x40, 0xF7,
|
|
||||||
0x8F, 0x7B, 0x97, 0xDA, 0xB1, 0xCF, 0x44, 0x94, 0xEC, 0x35, 0xD4, 0xCA, 0x5E, 0x2A, 0x1E, 0x02,
|
|
||||||
0xAA, 0x85, 0xD4, 0x67, 0xC4, 0x4D, 0xBD, 0x84, 0xC2, 0x66, 0xDB, 0x0B, 0x67, 0xDF, 0xEB, 0x8C,
|
|
||||||
0xFB, 0xF4, 0xDE, 0xD9, 0x6E, 0x36, 0xDB, 0x71, 0x94, 0x32, 0xC1, 0x22, 0xEE, 0x90, 0x61, 0x1A,
|
|
||||||
0x05, 0x99, 0xA0, 0xED, 0x80, 0x8E, 0x84, 0xF3, 0x3C, 0xBE, 0x6F, 0x0F, 0xA3, 0xC4, 0xA7, 0x89,
|
|
||||||
0xD3, 0x8A, 0xEF, 0x35, 0x00, 0x31, 0x5F, 0x7B, 0xB6, 0xB3, 0xB3, 0xD3, 0x1E, 0x12, 0xEF, 0x76,
|
|
||||||
0x9C, 0x44, 0x19, 0xF7, 0xEB, 0x5E, 0x14, 0x44, 0x89, 0xF3, 0x6C, 0xF4, 0x1C, 0xFF, 0xB4, 0x7D,
|
|
||||||
0x96, 0xC6, 0x01, 0x79, 0x70, 0x78, 0xC4, 0x29, 0xE0, 0xDE, 0xD7, 0xD3, 0x09, 0xF1, 0xA3, 0xA9,
|
|
||||||
0xD3, 0xD4, 0x9A, 0x5A, 0xAB, 0x09, 0x44, 0x92, 0xF1, 0x90, 0x18, 0x4D, 0x0B, 0xFF, 0xD8, 0x3B,
|
|
||||||
0x66, 0x7B, 0x14, 0x71, 0x51, 0x4F, 0xD9, 0x77, 0xEA, 0xB4, 0xB6, 0xE0, 0x34, 0x39, 0x1D, 0x91,
|
|
||||||
0x90, 0x05, 0x0F, 0x4E, 0x4A, 0x78, 0x5A, 0x4F, 0x69, 0xC2, 0x46, 0x6A, 0x79, 0x4A, 0xD9, 0x78,
|
|
||||||
0x22, 0x9C, 0xDF, 0x9A, 0xCD, 0x39, 0xF0, 0xAF, 0x65, 0xC1, 0x2C, 0x60, 0x29, 0x20, 0xA3, 0x78,
|
|
||||||
0xEA, 0x3C, 0x11, 0xC5, 0x4E, 0x53, 0xB1, 0xDE, 0x6C, 0x87, 0x24, 0x19, 0x33, 0x0E, 0x83, 0x98,
|
|
||||||
0xF8, 0x3E, 0xE3, 0x63, 0x47, 0xA1, 0x05, 0x6C, 0xB6, 0x90, 0x36, 0xA1, 0x01, 0x11, 0xEC, 0x8E,
|
|
||||||
0xB6, 0x43, 0xC6, 0xEB, 0x53, 0xE6, 0x8B, 0x89, 0xB3, 0x0B, 0x3C, 0xB6, 0xBD, 0x2C, 0x49, 0x41,
|
|
||||||
0xA6, 0x38, 0x62, 0x5C, 0xD0, 0x44, 0xA2, 0xA5, 0x31, 0xE1, 0xB3, 0x5C, 0x54, 0x54, 0x40, 0x21,
|
|
||||||
0x27, 0xE3, 0x01, 0xE3, 0xB4, 0x3E, 0x0C, 0x22, 0xEF, 0x76, 0x71, 0xD2, 0x6E, 0x7C, 0x9F, 0x9F,
|
|
||||||
0xE5, 0x4C, 0xA2, 0x3B, 0x9A, 0xCC, 0x96, 0xEA, 0x92, 0xD8, 0x15, 0x60, 0x85, 0x34, 0xA5, 0x74,
|
|
||||||
0x6E, 0x8B, 0xBB, 0x0C, 0xA0, 0x96, 0xFC, 0x05, 0x29, 0x17, 0xFC, 0x2F, 0x45, 0x5A, 0x11, 0x5C,
|
|
||||||
0xA1, 0x30, 0x1E, 0x67, 0x62, 0xF6, 0xF8, 0x2A, 0xA3, 0x98, 0x78, 0x4C, 0x3C, 0xA0, 0xFC, 0xB0,
|
|
||||||
0x6D, 0x86, 0xBA, 0x04, 0xAC, 0x24, 0x24, 0x81, 0x86, 0x3A, 0xD7, 0x3E, 0xD0, 0xC4, 0x27, 0x9C,
|
|
||||||
0x58, 0x9D, 0x84, 0x91, 0xC0, 0xEA, 0x2D, 0xB5, 0x5E, 0x0F, 0xA3, 0xEF, 0xF5, 0x0C, 0xC6, 0x30,
|
|
||||||
0x0F, 0xA8, 0x27, 0x94, 0x92, 0xE1, 0x1E, 0x86, 0xB7, 0x4C, 0x3C, 0x06, 0x3C, 0x5A, 0x28, 0xA9,
|
|
||||||
0x4B, 0x2A, 0x69, 0xA2, 0x2E, 0xB0, 0x25, 0xD5, 0x83, 0x1C, 0x4B, 0xC9, 0x95, 0x50, 0xF5, 0x61,
|
|
||||||
0x24, 0x44, 0x14, 0x4A, 0x93, 0x5B, 0x08, 0xAC, 0x49, 0xAB, 0x79, 0xF1, 0xE8, 0x46, 0xD6, 0x6B,
|
|
||||||
0xBF, 0x44, 0xBE, 0x0D, 0x7A, 0x15, 0xCC, 0x23, 0x41, 0x9D, 0x04, 0x6C, 0xCC, 0x9D, 0x90, 0xF9,
|
|
||||||
0x7E, 0x40, 0x4B, 0x56, 0xEB, 0x64, 0x49, 0x60, 0xF8, 0x44, 0x10, 0x87, 0x85, 0x64, 0x4C, 0x1B,
|
|
||||||
0x31, 0x1F, 0x03, 0x34, 0xA5, 0xBB, 0x3B, 0x16, 0xFB, 0xD0, 0xBD, 0xB8, 0x9A, 0x36, 0xDF, 0xBD,
|
|
||||||
0x1E, 0x47, 0x1D, 0xF8, 0xE7, 0xBC, 0x37, 0x98, 0x1C, 0x0F, 0xC6, 0x30, 0xEA, 0xE2, 0xB4, 0xF3,
|
|
||||||
0xFE, 0xB0, 0xF3, 0x1E, 0x7E, 0x0E, 0x5B, 0xB5, 0xAF, 0xA3, 0x6F, 0xB8, 0xD0, 0x7D, 0xED, 0x77,
|
|
||||||
0xFB, 0x83, 0xE3, 0x4E, 0xE7, 0x5D, 0xE3, 0xCD, 0xF9, 0xF4, 0xE3, 0xBB, 0x5D, 0x04, 0x77, 0x83,
|
|
||||||
0xE6, 0xD5, 0x87, 0x49, 0x73, 0xB0, 0xF5, 0x32, 0xF4, 0x4F, 0xFC, 0x89, 0x17, 0x0E, 0x3A, 0xEF,
|
|
||||||
0x3F, 0x5E, 0xDD, 0x5D, 0x87, 0x83, 0x71, 0xEF, 0x63, 0x6B, 0xF2, 0x79, 0xEB, 0x43, 0xEF, 0xF3,
|
|
||||||
0xC7, 0x57, 0xB7, 0xF4, 0xD3, 0xC9, 0xDB, 0xCF, 0xFD, 0x29, 0x20, 0x1C, 0x45, 0xBD, 0xC1, 0x55,
|
|
||||||
0xF7, 0x43, 0x77, 0xFC, 0xB9, 0xEB, 0x1D, 0xDF, 0x0F, 0x83, 0xF3, 0xEE, 0xEB, 0xCE, 0xB0, 0xB3,
|
|
||||||
0xE5, 0x51, 0x3A, 0xEE, 0x5F, 0x75, 0xB3, 0x37, 0xEF, 0x2E, 0xC6, 0x8C, 0x4D, 0x7A, 0x9F, 0xCF,
|
|
||||||
0xFB, 0xDE, 0xE1, 0xF3, 0xD3, 0xC1, 0x49, 0x87, 0x4D, 0xCE, 0xDF, 0x5E, 0x35, 0x6F, 0x5F, 0xBF,
|
|
||||||
0x3B, 0x3C, 0xF2, 0xAE, 0xDF, 0x5E, 0xEF, 0x1E, 0x6D, 0x37, 0x7E, 0xFB, 0xED, 0xCC, 0xBF, 0x60,
|
|
||||||
0xBC, 0x7F, 0xF7, 0xBD, 0x33, 0x3E, 0x9C, 0xBE, 0x78, 0x48, 0xFB, 0x93, 0x37, 0x77, 0xBC, 0xF1,
|
|
||||||
0x21, 0xFA, 0xFA, 0xE6, 0xE1, 0x0C, 0xFE, 0xBB, 0xBC, 0xAC, 0x0D, 0x7B, 0xAD, 0x74, 0xF0, 0xFE,
|
|
||||||
0xCD, 0x87, 0xAD, 0xF4, 0xE5, 0xF3, 0xB8, 0x7B, 0x74, 0x74, 0x17, 0x0E, 0x2F, 0x1B, 0xA1, 0x7F,
|
|
||||||
0x3B, 0x12, 0x2F, 0xB6, 0x45, 0x7C, 0x3D, 0xCE, 0x3E, 0x7F, 0x7B, 0xFE, 0x76, 0xD2, 0xB8, 0xA0,
|
|
||||||
0xE4, 0x7A, 0x52, 0x7B, 0xF8, 0xFE, 0xF0, 0x62, 0xD2, 0x3F, 0xB9, 0x3B, 0x0F, 0xC8, 0xFD, 0xF9,
|
|
||||||
0xB9, 0xF7, 0x3D, 0xAC, 0x05, 0xE4, 0xE5, 0x45, 0x3F, 0x20, 0x49, 0x6B, 0xE0, 0x77, 0x1A, 0xB5,
|
|
||||||
0xC3, 0xAD, 0xCE, 0x8E, 0x48, 0xAE, 0x0E, 0xF9, 0xD1, 0xF6, 0xD7, 0xDE, 0x8B, 0x6E, 0xB7, 0x15,
|
|
||||||
0x0D, 0xBF, 0x6D, 0xBD, 0xBE, 0xDD, 0x7D, 0x3D, 0xD8, 0x7D, 0x3F, 0x7C, 0xDF, 0xE9, 0xED, 0x74,
|
|
||||||
0x07, 0xE4, 0xBA, 0xF7, 0xBE, 0x33, 0xDA, 0x19, 0x4E, 0x26, 0xEF, 0xDE, 0xF5, 0x5F, 0xF9, 0x9D,
|
|
||||||
0xEF, 0x49, 0xE7, 0x62, 0xDA, 0xB9, 0x3F, 0x1E, 0x74, 0x4E, 0x6A, 0xEF, 0x8E, 0xCF, 0x9A, 0xAD,
|
|
||||||
0xDE, 0xF5, 0xF6, 0xF8, 0x6C, 0x77, 0xDA, 0x4D, 0x8F, 0x3B, 0xEF, 0xBB, 0xCD, 0xF1, 0xDB, 0x5A,
|
|
||||||
0x48, 0x3E, 0x47, 0x87, 0xDB, 0xE3, 0x37, 0xBB, 0xEC, 0xF2, 0x9A, 0x74, 0xDE, 0x74, 0xDF, 0xA6,
|
|
||||||
0xEC, 0x2A, 0x3C, 0x19, 0x34, 0x3B, 0x9D, 0xD3, 0x0B, 0xFA, 0xEA, 0x70, 0x9B, 0xBC, 0xDB, 0xF2,
|
|
||||||
0x3E, 0x82, 0xFE, 0x07, 0x9F, 0xE8, 0x6F, 0xB5, 0xCE, 0xF4, 0xA2, 0x19, 0x78, 0x2F, 0x69, 0xFF,
|
|
||||||
0xE4, 0xBA, 0x2F, 0x6F, 0xE7, 0x38, 0x78, 0xD5, 0xBF, 0xED, 0x65, 0xEF, 0xC3, 0xC3, 0x43, 0x53,
|
|
||||||
0xE3, 0x51, 0x3D, 0xA1, 0x31, 0x25, 0xA2, 0x1C, 0xAE, 0x16, 0xFE, 0x01, 0xB6, 0xB5, 0xB4, 0xC2,
|
|
||||||
0xDC, 0x4F, 0x05, 0xBD, 0x17, 0x75, 0x9F, 0x7A, 0x51, 0x42, 0xE4, 0x1E, 0x40, 0xA0, 0x09, 0x9A,
|
|
||||||
0xD8, 0xFC, 0x77, 0x19, 0x3F, 0x35, 0x15, 0x3F, 0x35, 0xC2, 0x7D, 0xCD, 0x28, 0x1C, 0x01, 0x83,
|
|
||||||
0x87, 0x4F, 0xEF, 0x98, 0x47, 0xEB, 0x31, 0xBB, 0xA7, 0x41, 0x5D, 0x22, 0x3B, 0x4D, 0x73, 0x26,
|
|
||||||
0xFD, 0xAD, 0xD8, 0x46, 0x38, 0x98, 0x9A, 0xA4, 0x5A, 0x2C, 0xF8, 0x5F, 0x89, 0x47, 0x21, 0xB0,
|
|
||||||
0x81, 0xCB, 0x84, 0xF8, 0xAB, 0x7C, 0x27, 0x4A, 0xEA, 0xC3, 0x6C, 0x3C, 0x62, 0xF7, 0xE0, 0xD0,
|
|
||||||
0x23, 0xC6, 0x99, 0xA0, 0x5A, 0x2B, 0x9D, 0xFF, 0x5E, 0x90, 0xB9, 0xA5, 0x0F, 0xA3, 0x84, 0x84,
|
|
||||||
0x34, 0xD5, 0xFE, 0x22, 0x99, 0xD9, 0x28, 0x89, 0xC2, 0x65, 0x10, 0x99, 0x8B, 0xA8, 0x34, 0x99,
|
|
||||||
0xCF, 0x9F, 0x65, 0x71, 0x10, 0x11, 0x10, 0x73, 0x4D, 0xE4, 0x50, 0xF1, 0x34, 0x91, 0x6E, 0xB5,
|
|
||||||
0x88, 0xAB, 0xB9, 0x9B, 0x6D, 0xA1, 0x5B, 0x96, 0xDD, 0x7A, 0x6B, 0x67, 0xE9, 0xBA, 0x75, 0xB9,
|
|
||||||
0x17, 0xE3, 0xFD, 0x9A, 0x4C, 0x81, 0xF1, 0xA0, 0x14, 0xEE, 0x9E, 0x09, 0x50, 0xE9, 0x13, 0x87,
|
|
||||||
0xCB, 0x43, 0xF2, 0xC8, 0xB0, 0x60, 0x40, 0x05, 0xEA, 0x96, 0x8C, 0xD4, 0x85, 0x24, 0xB0, 0x6F,
|
|
||||||
0xFE, 0x8C, 0xCA, 0xBC, 0x67, 0x3D, 0x8B, 0x13, 0xB8, 0x0D, 0x3A, 0xFD, 0x11, 0xCD, 0x42, 0xA6,
|
|
||||||
0x2A, 0x6D, 0x45, 0x53, 0x65, 0xBC, 0x5C, 0x84, 0x65, 0xDA, 0x93, 0xBC, 0x16, 0xA4, 0x1F, 0x4B,
|
|
||||||
0x05, 0xE0, 0x05, 0x37, 0xCF, 0x91, 0x9B, 0x1F, 0x6A, 0x75, 0x7B, 0xF7, 0x97, 0x9C, 0x87, 0x9D,
|
|
||||||
0xE6, 0x2F, 0x73, 0x3B, 0xDF, 0x5B, 0xA4, 0xE4, 0x56, 0x13, 0xFE, 0x29, 0x32, 0xEF, 0x8B, 0x25,
|
|
||||||
0x0B, 0xC3, 0xE7, 0xF8, 0xA7, 0x60, 0x10, 0xE9, 0x94, 0x80, 0xDB, 0x3B, 0x2F, 0x5F, 0xF8, 0xC3,
|
|
||||||
0x02, 0x98, 0x0B, 0xF6, 0x24, 0x3C, 0x21, 0x3E, 0xCB, 0x52, 0xE7, 0x79, 0xF3, 0x97, 0x5C, 0x9F,
|
|
||||||
0x5B, 0x3B, 0x28, 0xFB, 0xE2, 0x2E, 0x71, 0xB2, 0xB4, 0xD8, 0x34, 0x66, 0x5C, 0xDB, 0x4A, 0x35,
|
|
||||||
0xBC, 0x6F, 0x92, 0x2C, 0x0C, 0xB3, 0x92, 0xED, 0xE7, 0xBF, 0x2F, 0x4D, 0x13, 0xF7, 0xCF, 0x9A,
|
|
||||||
0xBF, 0xCC, 0x44, 0x02, 0xD9, 0x64, 0x04, 0xB9, 0xC6, 0x49, 0x22, 0x41, 0x04, 0x35, 0x9A, 0xE6,
|
|
||||||
0x1C, 0x84, 0x5B, 0x03, 0xD8, 0xDE, 0x6D, 0xFA, 0x74, 0x6C, 0xCE, 0xE7, 0x7B, 0x0D, 0x99, 0xD7,
|
|
||||||
0xA0, 0x6C, 0xF1, 0x12, 0x16, 0x8B, 0xFD, 0x51, 0xC6, 0x3D, 0xE4, 0x41, 0x1B, 0x53, 0x83, 0x9A,
|
|
||||||
0xB3, 0x84, 0x8A, 0x2C, 0xE1, 0x9A, 0x1F, 0x79, 0x19, 0x1A, 0xBB, 0x3D, 0xA6, 0xE2, 0x58, 0xD9,
|
|
||||||
0x7D, 0xF7, 0xE1, 0x8D, 0x0F, 0x3B, 0xE6, 0x0B, 0x04, 0x6F, 0x2D, 0x02, 0x38, 0x30, 0x9C, 0x97,
|
|
||||||
0xE3, 0x54, 0xF6, 0x43, 0x82, 0x01, 0x22, 0xEF, 0xE8, 0x83, 0x41, 0x2D, 0xB1, 0x40, 0xA4, 0x36,
|
|
||||||
0xAE, 0x1B, 0xC5, 0x2E, 0x80, 0x71, 0x73, 0x76, 0x07, 0x4A, 0x20, 0x2E, 0xFD, 0x22, 0x6E, 0x2C,
|
|
||||||
0xE6, 0x72, 0xF8, 0x69, 0xE7, 0xBB, 0xC9, 0x1E, 0x3B, 0xA8, 0xB7, 0x1C, 0xB2, 0xCF, 0x0E, 0x5A,
|
|
||||||
0xE0, 0x5E, 0x65, 0x6E, 0xE4, 0xB9, 0xAF, 0x58, 0x40, 0x07, 0xB9, 0xC3, 0xE1, 0x31, 0x48, 0x6C,
|
|
||||||
0xB1, 0x85, 0x28, 0xE2, 0x5B, 0xCD, 0xE6, 0x86, 0x4B, 0x0F, 0x48, 0x00, 0x39, 0xCC, 0xD0, 0x8F,
|
|
||||||
0xAF, 0xAE, 0x2E, 0xAE, 0xBE, 0xE8, 0x35, 0x5A, 0xD3, 0x6F, 0x1C, 0x4D, 0xAF, 0x71, 0xD3, 0x11,
|
|
||||||
0x76, 0x42, 0x47, 0x09, 0x4D, 0x27, 0x97, 0x44, 0x4C, 0x8C, 0xD4, 0xBE, 0x23, 0x41, 0x56, 0x16,
|
|
||||||
0x84, 0xA1, 0xDC, 0xC8, 0xA2, 0x70, 0x39, 0x9D, 0x6A, 0xAF, 0x40, 0xCD, 0x47, 0x90, 0xEA, 0xDA,
|
|
||||||
0xC2, 0x26, 0x71, 0x4C, 0xB9, 0x6F, 0xE8, 0x31, 0x20, 0xEA, 0x16, 0x35, 0xAD, 0x84, 0x7E, 0xCB,
|
|
||||||
0x68, 0x2A, 0x52, 0x1B, 0x2C, 0xD7, 0xD0, 0x2F, 0x07, 0x7D, 0xDD, 0xD2, 0x1B, 0xE8, 0x47, 0x3A,
|
|
||||||
0xF0, 0x46, 0xCC, 0x39, 0x52, 0x89, 0x5C, 0xD0, 0xA4, 0x3E, 0xCC, 0xC0, 0xA0, 0xB8, 0x6E, 0xB6,
|
|
||||||
0x23, 0x9B, 0x71, 0x4E, 0x93, 0x93, 0xFE, 0xD9, 0xA9, 0xAB, 0x5F, 0x29, 0x46, 0xB4, 0x53, 0x28,
|
|
||||||
0x48, 0x74, 0x4B, 0x5E, 0x51, 0x7E, 0xC8, 0xE1, 0x84, 0x05, 0xBE, 0x11, 0x99, 0x6D, 0x24, 0xE1,
|
|
||||||
0x49, 0x12, 0xB2, 0x40, 0x01, 0x0A, 0x9E, 0x2D, 0x1E, 0x62, 0xEA, 0xEA, 0x23, 0x50, 0x86, 0x6E,
|
|
||||||
0x79, 0x76, 0x98, 0x05, 0x82, 0xC5, 0x01, 0x75, 0x37, 0x5A, 0x30, 0xE3, 0x60, 0x41, 0xAE, 0x8E,
|
|
||||||
0xB9, 0x19, 0x61, 0xCC, 0x77, 0x75, 0x15, 0xA1, 0xF2, 0xB8, 0xB6, 0xEE, 0x14, 0x4F, 0x9D, 0x92,
|
|
||||||
0x56, 0x4E, 0x49, 0xCB, 0xB8, 0x4A, 0xE0, 0x34, 0x3F, 0x18, 0xC3, 0x3C, 0xCE, 0xD4, 0x51, 0x05,
|
|
||||||
0xCC, 0xA7, 0x23, 0x02, 0x9C, 0x7C, 0x40, 0x6D, 0xBA, 0x7A, 0x63, 0xDD, 0x41, 0xA9, 0x3A, 0xC8,
|
|
||||||
0xAF, 0x6A, 0xC4, 0x2F, 0x6B, 0x44, 0xDD, 0xEE, 0x3A, 0x64, 0x5F, 0x21, 0x07, 0x55, 0xE4, 0xA0,
|
|
||||||
0x8C, 0x7C, 0x28, 0x8D, 0x64, 0x1D, 0x72, 0xA0, 0x90, 0x93, 0x8A, 0x88, 0x89, 0x14, 0x51, 0x85,
|
|
||||||
0xBD, 0x3A, 0x6A, 0x13, 0x05, 0xD2, 0xAD, 0xA4, 0x22, 0x66, 0x62, 0x83, 0x97, 0x92, 0x61, 0x40,
|
|
||||||
0x7D, 0x77, 0xA3, 0x09, 0x33, 0x2C, 0xB6, 0xDD, 0xAD, 0xE6, 0x9A, 0x33, 0x12, 0x75, 0x46, 0x56,
|
|
||||||
0x65, 0x30, 0x2B, 0x33, 0xA8, 0xF5, 0xC8, 0x1D, 0xD5, 0xD6, 0x31, 0x98, 0x99, 0x56, 0x60, 0x47,
|
|
||||||
0xDC, 0x0B, 0x98, 0x77, 0xEB, 0x2E, 0xBD, 0xC5, 0x9C, 0xB1, 0x85, 0x85, 0x5A, 0x5C, 0x06, 0xBA,
|
|
||||||
0x01, 0x94, 0x5E, 0x8B, 0xA5, 0x7C, 0x80, 0xFA, 0x9E, 0x5B, 0xD9, 0x5A, 0x02, 0xDC, 0xA6, 0xF7,
|
|
||||||
0xD4, 0x3B, 0x8C, 0xC2, 0x90, 0xA0, 0xED, 0xA6, 0xC0, 0x41, 0x3E, 0xD1, 0xCD, 0xB9, 0x15, 0xAD,
|
|
||||||
0xC5, 0x79, 0xC2, 0x45, 0x2C, 0x7F, 0x3D, 0x8B, 0x23, 0x03, 0x5C, 0xCE, 0xF5, 0x6C, 0xD4, 0x61,
|
|
||||||
0x6A, 0x83, 0x1E, 0xC7, 0x62, 0xF2, 0x13, 0x17, 0x2A, 0x0C, 0x54, 0xA2, 0x7C, 0x69, 0xDE, 0x58,
|
|
||||||
0x0B, 0x91, 0x56, 0x7C, 0xEA, 0xA2, 0xB7, 0xE2, 0x54, 0xA8, 0xBC, 0x8A, 0x5D, 0x9A, 0x4B, 0x1D,
|
|
||||||
0x94, 0x61, 0xB9, 0xBD, 0x2F, 0xA0, 0xFA, 0x7C, 0x0E, 0xE7, 0x01, 0xFF, 0x13, 0x68, 0xF9, 0xE8,
|
|
||||||
0x5F, 0x17, 0x60, 0xC9, 0xA3, 0x34, 0x78, 0x8B, 0xBB, 0x0D, 0xE3, 0xC0, 0xF9, 0x8F, 0x6D, 0x7C,
|
|
||||||
0xF9, 0x1F, 0xFB, 0xA6, 0x66, 0x9A, 0x07, 0xFF, 0x6A, 0x48, 0x0D, 0x1B, 0xC2, 0xFC, 0xD2, 0xBA,
|
|
||||||
0xB1, 0x08, 0x80, 0xED, 0x7F, 0x9B, 0xFF, 0xB1, 0x25, 0xB8, 0x02, 0x6B, 0xDF, 0x45, 0x90, 0x49,
|
|
||||||
0xF0, 0x24, 0x34, 0xB0, 0x68, 0xA4, 0x91, 0xCD, 0x4D, 0x43, 0xB8, 0xA4, 0x72, 0x8D, 0x35, 0x51,
|
|
||||||
0xD3, 0x6D, 0x88, 0x53, 0x50, 0x5B, 0xAC, 0x04, 0xBF, 0x3E, 0x24, 0x7A, 0x15, 0x5B, 0x17, 0x00,
|
|
||||||
0xC9, 0x3D, 0xCA, 0x0C, 0x3D, 0x22, 0x97, 0x52, 0xCB, 0x0C, 0x02, 0x42, 0xA7, 0x89, 0xE7, 0x2A,
|
|
||||||
0xAD, 0x1D, 0x14, 0x30, 0x17, 0xA2, 0xE0, 0xBC, 0x1C, 0x2D, 0x15, 0xEA, 0xAA, 0xFD, 0x17, 0x0A,
|
|
||||||
0xA3, 0xD6, 0x12, 0x8A, 0x04, 0x31, 0xAD, 0xD8, 0x79, 0xC6, 0x72, 0x75, 0x4C, 0x59, 0xBA, 0x35,
|
|
||||||
0x59, 0x5D, 0x96, 0xAD, 0x04, 0xAE, 0x2F, 0x8D, 0xFE, 0xD7, 0x3D, 0x16, 0x8E, 0xB5, 0x12, 0x3F,
|
|
||||||
0xF8, 0x97, 0xFB, 0x2B, 0x46, 0xE4, 0xCD, 0x3F, 0xBC, 0x21, 0x70, 0x05, 0xA6, 0x41, 0x6D, 0x1E,
|
|
||||||
0x4D, 0x0D, 0xB3, 0xF6, 0xAB, 0xAE, 0x49, 0x8A, 0xAE, 0x1E, 0x92, 0xFB, 0xBC, 0xA7, 0xC4, 0x8C,
|
|
||||||
0xD7, 0xD6, 0x70, 0x5E, 0xB4, 0x28, 0xF9, 0x82, 0xEC, 0xE6, 0x48, 0x26, 0xA2, 0xB6, 0x56, 0x64,
|
|
||||||
0x52, 0xD5, 0xCA, 0xE8, 0x5A, 0x63, 0xFF, 0xD7, 0x4A, 0x40, 0xB7, 0x98, 0xBA, 0x4E, 0x15, 0x8C,
|
|
||||||
0xB3, 0x00, 0x1C, 0x93, 0x3E, 0x1D, 0x69, 0x03, 0x26, 0x03, 0x75, 0x35, 0x46, 0x5A, 0x81, 0xC1,
|
|
||||||
0xCC, 0x03, 0xC3, 0x2B, 0xFB, 0xF3, 0x1E, 0x16, 0xBF, 0xFB, 0x97, 0xAA, 0xAA, 0x81, 0xD4, 0x8B,
|
|
||||||
0x33, 0x5D, 0x59, 0x59, 0xD5, 0x4B, 0xE0, 0xD2, 0x08, 0xA0, 0x5B, 0x8B, 0x3C, 0x3A, 0x8C, 0xFC,
|
|
||||||
0x87, 0x52, 0xF6, 0x4D, 0xBB, 0x0F, 0x87, 0x01, 0x49, 0xD3, 0x73, 0xB8, 0x01, 0x43, 0xF7, 0x42,
|
|
||||||
0x50, 0xB8, 0xB2, 0xC2, 0xFD, 0xE6, 0xE6, 0x66, 0x15, 0x29, 0xA1, 0x21, 0x14, 0xDB, 0x8A, 0x2B,
|
|
||||||
0xF0, 0x49, 0xD3, 0xF1, 0x81, 0x30, 0x18, 0xD2, 0x1A, 0xC6, 0xF0, 0x25, 0xE3, 0x47, 0x5C, 0x71,
|
|
||||||
0xF4, 0xF4, 0x22, 0xA6, 0xFC, 0x33, 0xDC, 0x95, 0x32, 0xCB, 0x1A, 0xAD, 0xA6, 0x68, 0xFA, 0x8F,
|
|
||||||
0xD8, 0x3E, 0xCA, 0x0D, 0x76, 0xC1, 0x7A, 0xBA, 0x56, 0xA1, 0xFC, 0x9F, 0x61, 0xB9, 0x94, 0x28,
|
|
||||||
0xD6, 0x70, 0x9C, 0x40, 0x80, 0x5A, 0xC3, 0x31, 0xC4, 0x1A, 0x41, 0x17, 0xFC, 0x26, 0x6B, 0xF9,
|
|
||||||
0xCD, 0xFE, 0x19, 0x7E, 0x97, 0x76, 0x1E, 0x15, 0x25, 0x91, 0xAA, 0xAF, 0x50, 0x02, 0x9F, 0xDD,
|
|
||||||
0xE9, 0xA6, 0x15, 0xB9, 0x55, 0x0A, 0x50, 0x1B, 0x46, 0x41, 0xD0, 0x8F, 0xE2, 0x83, 0x27, 0xD6,
|
|
||||||
0x9D, 0xC5, 0x7A, 0x31, 0xC8, 0xD9, 0x5C, 0x6E, 0xB1, 0xBC, 0xB5, 0x44, 0x4F, 0xA1, 0xEC, 0x5F,
|
|
||||||
0x4B, 0x15, 0x01, 0x3F, 0x23, 0x8B, 0x7B, 0xAC, 0xD4, 0xA5, 0x36, 0x28, 0x0F, 0x56, 0x3F, 0xD5,
|
|
||||||
0x3C, 0xCB, 0x5F, 0xCC, 0xAE, 0x6B, 0x51, 0x9B, 0xC0, 0x38, 0x57, 0x92, 0x8B, 0x4A, 0xB2, 0xC8,
|
|
||||||
0x13, 0x01, 0xA8, 0x58, 0xC7, 0x2E, 0xC4, 0x4D, 0x6B, 0x7A, 0x7C, 0xBF, 0x5C, 0x83, 0xC2, 0xDF,
|
|
||||||
0xF5, 0xD5, 0x12, 0x33, 0x08, 0xC4, 0xD3, 0x95, 0x4B, 0x29, 0x5F, 0x37, 0x29, 0x8A, 0x0E, 0x62,
|
|
||||||
0x47, 0xA3, 0x51, 0x4A, 0xC5, 0x47, 0x0C, 0x49, 0x56, 0xB2, 0x98, 0x9F, 0xC8, 0x90, 0x04, 0x8C,
|
|
||||||
0x45, 0x3C, 0x8C, 0xB2, 0x94, 0x46, 0x99, 0xA8, 0xA4, 0x16, 0x63, 0x21, 0xCC, 0x5E, 0xFA, 0xE7,
|
|
||||||
0x9F, 0x8B, 0xC9, 0x7E, 0x5A, 0x0B, 0x96, 0xD3, 0xEB, 0x3D, 0xBF, 0x34, 0xD9, 0xF7, 0x6B, 0x89,
|
|
||||||
0xB9, 0x7A, 0xE9, 0xFF, 0x67, 0x4B, 0x21, 0x65, 0x4B, 0xF1, 0xB0, 0x54, 0x2E, 0x62, 0x62, 0x29,
|
|
||||||
0xE6, 0xC9, 0x82, 0x91, 0x97, 0x7C, 0x16, 0x0D, 0x1A, 0x2B, 0x25, 0x55, 0x9E, 0x97, 0x7D, 0x95,
|
|
||||||
0x43, 0x40, 0x59, 0x71, 0xE5, 0x35, 0x11, 0x06, 0x34, 0xE0, 0x63, 0x64, 0xF2, 0x41, 0xEB, 0xA7,
|
|
||||||
0xD1, 0x94, 0x26, 0x87, 0x24, 0xA5, 0x06, 0x24, 0xCD, 0x65, 0xDC, 0x41, 0xA8, 0xE9, 0x04, 0xEB,
|
|
||||||
0x76, 0x6D, 0x6E, 0x12, 0x05, 0xCE, 0x33, 0x77, 0xC4, 0xB1, 0x26, 0x03, 0xF9, 0xB2, 0xCA, 0x09,
|
|
||||||
0xD4, 0xC6, 0xBE, 0x12, 0xA4, 0x3E, 0x52, 0x25, 0xA8, 0x61, 0x5A, 0xD0, 0x76, 0xC0, 0x35, 0x5F,
|
|
||||||
0x26, 0x51, 0x4C, 0xC6, 0xB2, 0x07, 0x83, 0x35, 0x74, 0x0F, 0xA4, 0x66, 0x6D, 0x34, 0x91, 0x60,
|
|
||||||
0xA9, 0x73, 0x29, 0xFC, 0x66, 0xD9, 0xC2, 0x70, 0x4B, 0x57, 0xC9, 0xB0, 0xBD, 0xF4, 0xA5, 0x35,
|
|
||||||
0x59, 0x83, 0xE0, 0x0B, 0x6C, 0x62, 0xE0, 0x1E, 0x68, 0x64, 0xF2, 0x7B, 0x00, 0x77, 0x6B, 0xB6,
|
|
||||||
0xA3, 0x3D, 0xD6, 0x8E, 0x6A, 0x35, 0x53, 0x55, 0xE9, 0xAE, 0x0B, 0x6D, 0x4E, 0x74, 0x23, 0x0B,
|
|
||||||
0x4B, 0x10, 0xAA, 0x9A, 0x59, 0x0C, 0x38, 0x1B, 0x81, 0xAA, 0xBA, 0xC0, 0x11, 0xD6, 0x98, 0x66,
|
|
||||||
0xA9, 0x23, 0xF1, 0x97, 0x1D, 0xC9, 0x13, 0xB5, 0x07, 0x95, 0xF5, 0x05, 0xD4, 0x31, 0xAB, 0x25,
|
|
||||||
0x86, 0x30, 0xD3, 0x29, 0x13, 0xDE, 0x04, 0x03, 0x90, 0x07, 0x5A, 0xD5, 0x05, 0x14, 0xB5, 0x8E,
|
|
||||||
0x1C, 0x4D, 0x44, 0xB8, 0x1C, 0x05, 0xF9, 0xF0, 0x6B, 0x9A, 0x0F, 0xBC, 0xB4, 0x18, 0xDD, 0x97,
|
|
||||||
0x80, 0x50, 0xD2, 0xE6, 0xE0, 0x88, 0x8F, 0xF2, 0x21, 0xF4, 0xB2, 0x05, 0x9D, 0x02, 0x58, 0xFC,
|
|
||||||
0xC6, 0x71, 0x3E, 0x8A, 0x27, 0xC5, 0x68, 0x42, 0xEF, 0x17, 0x78, 0x51, 0x01, 0xF5, 0xA9, 0xEE,
|
|
||||||
0x28, 0x1B, 0xDB, 0x68, 0xCE, 0xF3, 0x41, 0x6B, 0x29, 0x7F, 0xF0, 0xFF, 0x28, 0x7F, 0xCC, 0xC7,
|
|
||||||
0x85, 0x34, 0x71, 0x31, 0x1A, 0xB3, 0x42, 0x96, 0x61, 0x18, 0xFF, 0x90, 0x93, 0xA4, 0xD4, 0x13,
|
|
||||||
0x97, 0x7A, 0x5A, 0xF1, 0xB3, 0xB6, 0x53, 0x98, 0x8E, 0x31, 0xAA, 0xF8, 0xE3, 0xC8, 0xF6, 0xF0,
|
|
||||||
0xF7, 0x3C, 0xF2, 0x65, 0x6D, 0x69, 0x5A, 0xA1, 0x31, 0x82, 0x3A, 0x57, 0x37, 0xCB, 0x7E, 0x9A,
|
|
||||||
0xFD, 0xB7, 0xAD, 0xE8, 0xD1, 0xF1, 0xE9, 0x71, 0xFF, 0xB8, 0x5C, 0x38, 0x23, 0xE7, 0x25, 0x93,
|
|
||||||
0x8A, 0x2B, 0x5D, 0xFA, 0xB2, 0x22, 0x80, 0x02, 0x1B, 0x45, 0x01, 0x7B, 0xDD, 0xDC, 0x54, 0x7E,
|
|
||||||
0xF1, 0xB6, 0x77, 0x71, 0x6E, 0xC7, 0x24, 0x01, 0x8F, 0x24, 0x15, 0xE6, 0xC2, 0x82, 0x44, 0xF9,
|
|
||||||
0xE0, 0xD7, 0xC7, 0xA5, 0x72, 0x5D, 0x7E, 0x61, 0x70, 0xC4, 0xDC, 0x52, 0xA7, 0xA9, 0x7E, 0x78,
|
|
||||||
0xE2, 0x62, 0x5D, 0x99, 0xBF, 0x04, 0x41, 0x72, 0x1A, 0x2D, 0x13, 0x55, 0x11, 0x67, 0x46, 0xE5,
|
|
||||||
0x30, 0x2F, 0xEE, 0xB2, 0x75, 0x0D, 0xD3, 0xC8, 0xB4, 0xC4, 0x84, 0xA5, 0xE5, 0x46, 0xA5, 0x12,
|
|
||||||
0x14, 0xFE, 0xA2, 0xB6, 0xE7, 0x8B, 0x91, 0x24, 0xB7, 0x5A, 0x73, 0xAB, 0x6F, 0x41, 0x2A, 0x3E,
|
|
||||||
0x58, 0x04, 0x23, 0x66, 0x39, 0xDB, 0x16, 0x77, 0xA3, 0x43, 0xEE, 0x61, 0x5C, 0x7F, 0xBA, 0x35,
|
|
||||||
0x78, 0xD2, 0x3C, 0x79, 0x61, 0x9E, 0xFC, 0xB1, 0x7B, 0x2E, 0x1C, 0x45, 0xF9, 0xDA, 0xE2, 0x98,
|
|
||||||
0xF6, 0x10, 0x58, 0xBB, 0x6D, 0x2F, 0x7D, 0x18, 0x20, 0xD2, 0x83, 0xCB, 0x00, 0xF4, 0x63, 0x58,
|
|
||||||
0xFF, 0x4A, 0xEE, 0x88, 0x7A, 0x09, 0xAA, 0xA2, 0xAD, 0x73, 0x54, 0xD8, 0xEE, 0xFD, 0x81, 0xA3,
|
|
||||||
0xF2, 0xCE, 0x65, 0x18, 0x48, 0x97, 0xC3, 0x92, 0x37, 0x8B, 0x75, 0xC1, 0x61, 0x19, 0x31, 0x64,
|
|
||||||
0x6C, 0x00, 0xE3, 0xCD, 0x5D, 0x49, 0x13, 0xD5, 0x1C, 0xB4, 0xF0, 0x1B, 0x08, 0x8A, 0x4F, 0x39,
|
|
||||||
0xCE, 0x9A, 0x38, 0xAD, 0x62, 0x72, 0xC5, 0x23, 0xC8, 0x4A, 0x67, 0x89, 0xC0, 0x6E, 0x10, 0x0D,
|
|
||||||
0x0D, 0x7C, 0x64, 0x9A, 0xA1, 0xB6, 0x1D, 0x3E, 0x37, 0xD7, 0xBC, 0xD9, 0x54, 0xFA, 0x4B, 0x62,
|
|
||||||
0x79, 0xD5, 0xB0, 0x8B, 0x1C, 0x56, 0xCC, 0x75, 0x7D, 0x1F, 0xF4, 0xA3, 0x4E, 0x29, 0xAF, 0x48,
|
|
||||||
0xA4, 0x53, 0xD1, 0x83, 0xC4, 0x86, 0xA2, 0x41, 0xBE, 0x91, 0x40, 0x44, 0x72, 0x4A, 0x33, 0x5D,
|
|
||||||
0xC7, 0xCA, 0xD2, 0x0B, 0x28, 0x49, 0x7A, 0xB2, 0x73, 0x95, 0x49, 0x6B, 0x25, 0x06, 0xFE, 0xC8,
|
|
||||||
0xD7, 0xF0, 0xC7, 0xA1, 0xD0, 0xA3, 0x83, 0x9B, 0x49, 0x2B, 0x83, 0xA4, 0x23, 0x64, 0x83, 0xA9,
|
|
||||||
0x37, 0xE4, 0xBB, 0xA8, 0x2D, 0x2F, 0xCB, 0xB4, 0x16, 0x50, 0x70, 0x71, 0x83, 0xBB, 0x11, 0x30,
|
|
||||||
0x52, 0x5A, 0xC4, 0x9E, 0x94, 0xA8, 0xC7, 0x8F, 0x10, 0x1F, 0x53, 0x4A, 0x20, 0x06, 0x20, 0xA6,
|
|
||||||
0x40, 0xD0, 0xA7, 0x42, 0x8A, 0x54, 0xE6, 0x92, 0x53, 0x2A, 0x20, 0xCA, 0x48, 0xCD, 0xE2, 0xC1,
|
|
||||||
0x85, 0x78, 0xD4, 0x46, 0xD6, 0x80, 0xFD, 0xDC, 0xBD, 0x73, 0x33, 0xDE, 0x90, 0x68, 0x09, 0x56,
|
|
||||||
0x36, 0x3D, 0x9A, 0xA6, 0x52, 0x5C, 0x54, 0xC7, 0x19, 0xF8, 0xA8, 0xA1, 0x03, 0x5A, 0x23, 0x84,
|
|
||||||
0x11, 0x1E, 0x84, 0x8A, 0x01, 0x40, 0x7F, 0x42, 0xC3, 0x1C, 0x22, 0x70, 0x08, 0x20, 0x82, 0xA0,
|
|
||||||
0x7F, 0x49, 0x0D, 0xF7, 0x64, 0x05, 0xC9, 0xF8, 0xD8, 0x6D, 0x35, 0xF0, 0x9D, 0x66, 0x95, 0xEC,
|
|
||||||
0x20, 0xA5, 0xBD, 0x68, 0x24, 0xFA, 0x64, 0x98, 0x1A, 0x50, 0x00, 0xAC, 0xD9, 0x01, 0xA0, 0x1E,
|
|
||||||
0x24, 0x5E, 0x63, 0x2B, 0x3F, 0xEF, 0x04, 0x2A, 0xBB, 0x00, 0xAB, 0xBB, 0x8E, 0x87, 0x5F, 0x39,
|
|
||||||
0x4F, 0x19, 0xA7, 0x39, 0x26, 0x00, 0x7B, 0x93, 0x68, 0x7A, 0x99, 0x30, 0x2E, 0xCE, 0x64, 0x1B,
|
|
||||||
0x6A, 0x6C, 0xB4, 0xE4, 0xF5, 0xA9, 0x87, 0x15, 0x79, 0x3F, 0xC5, 0x8B, 0xCB, 0x0C, 0xF3, 0xBA,
|
|
||||||
0x53, 0x79, 0x77, 0xB1, 0x86, 0x70, 0x21, 0x50, 0x66, 0x38, 0xB3, 0x29, 0x74, 0xB0, 0xFA, 0xA1,
|
|
||||||
0x48, 0x82, 0x7A, 0x4F, 0xB7, 0x42, 0xE2, 0xC1, 0x44, 0xED, 0x81, 0xF9, 0xDC, 0xC2, 0xD8, 0xE1,
|
|
||||||
0x94, 0x83, 0x5A, 0x0A, 0xB5, 0x02, 0x45, 0xC6, 0x95, 0xCD, 0x98, 0x35, 0x1D, 0x6A, 0x58, 0x88,
|
|
||||||
0x61, 0xE0, 0xAF, 0xFE, 0x05, 0x0F, 0x1E, 0x1C, 0xC8, 0x55, 0x3F, 0xE1, 0x23, 0xE3, 0x7E, 0xF4,
|
|
||||||
0x23, 0x3E, 0x3E, 0xAF, 0xF0, 0xF1, 0x79, 0x1D, 0x1F, 0xB4, 0xAA, 0x3C, 0x98, 0x0C, 0x80, 0xEC,
|
|
||||||
0x19, 0xE1, 0x64, 0x4C, 0x13, 0x58, 0xC0, 0x43, 0x50, 0x25, 0x7F, 0x8B, 0xB3, 0x84, 0xFE, 0x98,
|
|
||||||
0xB3, 0xDE, 0x84, 0x8D, 0xC4, 0x23, 0xFE, 0x8A, 0xD5, 0xFF, 0x82, 0x4B, 0x3C, 0x70, 0x3D, 0x97,
|
|
||||||
0x79, 0x6D, 0x5A, 0x49, 0x28, 0x3F, 0x7E, 0x2B, 0x91, 0x7E, 0xE4, 0x42, 0x78, 0xA9, 0x38, 0xC8,
|
|
||||||
0xDF, 0xB7, 0xF4, 0x00, 0xBC, 0x11, 0xF8, 0x29, 0x35, 0x75, 0xBC, 0x0B, 0xA5, 0xFC, 0x29, 0x30,
|
|
||||||
0x64, 0xA8, 0xC0, 0x47, 0xDD, 0xD9, 0xDC, 0x12, 0xAE, 0x01, 0x8A, 0xF1, 0xA3, 0x29, 0xB0, 0xEA,
|
|
||||||
0xC9, 0x02, 0xD7, 0x9E, 0x40, 0x26, 0x04, 0x91, 0xE0, 0x48, 0xC8, 0xA7, 0x8D, 0x2F, 0x07, 0x9B,
|
|
||||||
0x37, 0x35, 0xC8, 0x43, 0x2E, 0xFC, 0x98, 0x2E, 0x0C, 0x36, 0x6F, 0xFE, 0x6D, 0x36, 0xC6, 0xCC,
|
|
||||||
0x5A, 0x76, 0xA4, 0x96, 0x4C, 0xF6, 0xF4, 0x0B, 0xBF, 0x71, 0x09, 0x48, 0x5D, 0x49, 0x78, 0x45,
|
|
||||||
0x34, 0x03, 0x6B, 0x43, 0x61, 0xE1, 0x07, 0xFF, 0x47, 0x09, 0xF8, 0x91, 0x9E, 0x07, 0xCE, 0xBD,
|
|
||||||
0xE6, 0x3D, 0x5E, 0x2F, 0x3E, 0x85, 0xE9, 0x56, 0xE9, 0xC1, 0x4A, 0xC7, 0xEF, 0x53, 0x3A, 0x76,
|
|
||||||
0x59, 0xA2, 0x14, 0x4A, 0x14, 0x59, 0x88, 0x1A, 0x6A, 0x50, 0x0E, 0x51, 0x98, 0x89, 0x17, 0xCD,
|
|
||||||
0x81, 0x02, 0x9B, 0x73, 0x34, 0x5B, 0x3A, 0x02, 0x0F, 0xF4, 0xF5, 0x45, 0xEE, 0xFC, 0x74, 0x76,
|
|
||||||
0x7A, 0x22, 0x44, 0x7C, 0xA5, 0x62, 0x22, 0xD0, 0xAA, 0x2E, 0x2C, 0x2F, 0xCF, 0x9C, 0x89, 0xE4,
|
|
||||||
0xA1, 0x28, 0x75, 0x30, 0x31, 0x28, 0x87, 0xFE, 0x74, 0x31, 0xFC, 0x0A, 0x71, 0xD6, 0xD0, 0xCF,
|
|
||||||
0x52, 0x48, 0x58, 0x5B, 0x36, 0xA2, 0xF7, 0xFB, 0x97, 0xF6, 0xAE, 0xDD, 0x84, 0xBA, 0x00, 0xB4,
|
|
||||||
0x0A, 0x69, 0x19, 0xEE, 0x7D, 0xFE, 0xB7, 0x90, 0xB7, 0xFF, 0x1E, 0x32, 0x83, 0xA8, 0x95, 0x42,
|
|
||||||
0x58, 0x2A, 0xF0, 0xAB, 0xB8, 0x93, 0x24, 0x9A, 0x4A, 0xB4, 0xE3, 0x24, 0xC1, 0x4B, 0xE9, 0x43,
|
|
||||||
0x85, 0xA2, 0x0D, 0x61, 0x31, 0xA5, 0x89, 0xE6, 0x47, 0x34, 0xD5, 0x78, 0x24, 0xB4, 0x34, 0x8B,
|
|
||||||
0x63, 0x68, 0x5C, 0x56, 0xF4, 0x61, 0xEB, 0xC5, 0xEB, 0xCB, 0xFB, 0x8C, 0x66, 0xD4, 0xCF, 0x97,
|
|
||||||
0x69, 0x52, 0xD1, 0x0B, 0x56, 0x50, 0xDF, 0x10, 0xEE, 0x7E, 0xB9, 0xC9, 0xEB, 0xA9, 0x8C, 0x73,
|
|
||||||
0x8C, 0xA2, 0x1B, 0x2D, 0x35, 0x07, 0xE9, 0x26, 0x40, 0xD5, 0xE5, 0x59, 0x10, 0xCC, 0xDB, 0x2B,
|
|
||||||
0xB4, 0xA0, 0xF1, 0x8A, 0x44, 0x24, 0x9F, 0xCB, 0x67, 0x7F, 0xE4, 0xC9, 0xA9, 0xE2, 0x82, 0x50,
|
|
||||||
0xF2, 0x54, 0xA9, 0x36, 0xAD, 0x0D, 0x63, 0x83, 0x6A, 0x8C, 0xA7, 0x82, 0x70, 0x0F, 0xAF, 0x51,
|
|
||||||
0xE9, 0xC2, 0x2C, 0x6A, 0x29, 0xDC, 0xDE, 0x46, 0x5F, 0xCB, 0x6D, 0xE9, 0x89, 0x7C, 0x2A, 0x25,
|
|
||||||
0xE3, 0xAE, 0xAE, 0x63, 0x55, 0x45, 0xB1, 0x3E, 0x25, 0x61, 0x5A, 0x26, 0x5B, 0x54, 0x06, 0x26,
|
|
||||||
0x77, 0x0B, 0x70, 0x9B, 0x06, 0x29, 0x1C, 0xBD, 0x7E, 0x7F, 0xCE, 0x46, 0xD1, 0xCE, 0x11, 0x80,
|
|
||||||
0x69, 0xC5, 0x3E, 0x93, 0xD7, 0xE0, 0x24, 0xCC, 0x73, 0x07, 0x32, 0xE9, 0x4A, 0x03, 0x0E, 0xA9,
|
|
||||||
0x98, 0x44, 0xFE, 0x81, 0x7E, 0xA0, 0x3B, 0x3A, 0xFC, 0xBB, 0x09, 0x35, 0x47, 0xCD, 0xA5, 0xD0,
|
|
||||||
0xA4, 0xFA, 0x74, 0x70, 0xF5, 0x06, 0xC2, 0x53, 0x0C, 0xA5, 0x01, 0x17, 0x50, 0x34, 0xD7, 0x74,
|
|
||||||
0x7C, 0x7A, 0x7D, 0x0C, 0x29, 0xC8, 0x7F, 0x21, 0x37, 0x66, 0xBB, 0xAA, 0x6C, 0xB8, 0xF3, 0xEA,
|
|
||||||
0x75, 0x56, 0x2E, 0x03, 0x7A, 0x61, 0x8C, 0x58, 0x0F, 0x29, 0x7E, 0xFB, 0x7B, 0xF4, 0x9E, 0x8D,
|
|
||||||
0x15, 0xD2, 0x6A, 0x5D, 0x6F, 0xCE, 0x76, 0x90, 0x67, 0x89, 0xD5, 0x43, 0x2C, 0x70, 0x97, 0x1F,
|
|
||||||
0x29, 0x59, 0x95, 0x35, 0xDC, 0xF6, 0x48, 0x10, 0xE0, 0xC7, 0x5A, 0x03, 0x1B, 0x6A, 0x22, 0xB2,
|
|
||||||
0xD4, 0x42, 0x22, 0x29, 0x08, 0x90, 0xD2, 0x3E, 0x84, 0x39, 0xD3, 0x92, 0x65, 0x86, 0xB2, 0xA1,
|
|
||||||
0xBC, 0xFF, 0xC5, 0x9A, 0xA3, 0x64, 0x46, 0xE8, 0xCE, 0xF9, 0x6C, 0x73, 0x53, 0xD8, 0x85, 0x99,
|
|
||||||
0x18, 0x05, 0x52, 0x8A, 0x01, 0x1C, 0x9A, 0x7D, 0x68, 0x2D, 0x8C, 0xB2, 0x90, 0x58, 0xAB, 0x3D,
|
|
||||||
0xD2, 0xB6, 0x51, 0x55, 0x03, 0x54, 0x7C, 0x46, 0x01, 0x03, 0xCE, 0xB2, 0x24, 0x80, 0xA8, 0x8B,
|
|
||||||
0x39, 0xBA, 0xB2, 0x2D, 0xC5, 0xBA, 0xD0, 0x84, 0x0E, 0xEC, 0x67, 0xC8, 0x12, 0x95, 0x97, 0xAD,
|
|
||||||
0xA2, 0x27, 0x12, 0xC5, 0x77, 0x95, 0x9E, 0xC8, 0x6F, 0xE5, 0x84, 0xAA, 0xC8, 0x77, 0x88, 0x2F,
|
|
||||||
0x13, 0x5C, 0xD4, 0xD1, 0x13, 0xA0, 0x24, 0x83, 0x52, 0x34, 0x60, 0x2A, 0x2C, 0x37, 0xEE, 0xEB,
|
|
||||||
0xD3, 0xE9, 0xB4, 0x8E, 0xDF, 0x6A, 0xEB, 0x70, 0x82, 0xB2, 0x02, 0x5F, 0x5F, 0xC7, 0x21, 0x47,
|
|
||||||
0x15, 0x58, 0xF8, 0x6E, 0xE1, 0xAC, 0xBA, 0xE8, 0x42, 0x7F, 0x2B, 0xDE, 0xD4, 0xAA, 0xD2, 0x59,
|
|
||||||
0xE1, 0x73, 0x79, 0xDB, 0x7B, 0x3B, 0x2B, 0x20, 0x32, 0xC4, 0xAF, 0xB2, 0x90, 0x69, 0x20, 0x0D,
|
|
||||||
0x3B, 0xE5, 0x46, 0x56, 0x25, 0x85, 0x65, 0x5C, 0xB0, 0xE3, 0x2C, 0x9D, 0x18, 0x33, 0x60, 0xDD,
|
|
||||||
0x11, 0x96, 0xD2, 0x95, 0x43, 0x2D, 0x65, 0xB7, 0x0E, 0xB7, 0x0A, 0xFB, 0x70, 0x30, 0x83, 0x94,
|
|
||||||
0x79, 0xFB, 0xF3, 0x4F, 0x39, 0x5B, 0xDE, 0xF6, 0x92, 0x62, 0x71, 0xE1, 0xF3, 0xFC, 0xA9, 0x35,
|
|
||||||
0xAF, 0x69, 0xA5, 0xD1, 0xAF, 0xC4, 0x97, 0xBD, 0x46, 0xFE, 0x19, 0x3B, 0xFF, 0x9C, 0xAD, 0x81,
|
|
||||||
0xB1, 0x43, 0x23, 0x2A, 0xDC, 0x4C, 0x8C, 0xEA, 0x2F, 0x34, 0xE6, 0x63, 0x79, 0x29, 0xBF, 0x2D,
|
|
||||||
0xA0, 0x54, 0xA9, 0xD3, 0x68, 0x78, 0x3E, 0xFF, 0x9A, 0x42, 0x19, 0x1D, 0x65, 0xFE, 0x28, 0x20,
|
|
||||||
0x09, 0xC5, 0x82, 0xA3, 0x41, 0xBE, 0x92, 0xFB, 0x46, 0xC0, 0x86, 0x69, 0x03, 0x93, 0x6D, 0xCB,
|
|
||||||
0xDE, 0xB2, 0x77, 0x71, 0x64, 0x7F, 0x4D, 0xF7, 0x57, 0x4F, 0xD8, 0x5F, 0x34, 0x69, 0x58, 0x0B,
|
|
||||||
0xE7, 0xB5, 0xAB, 0x8A, 0x4D, 0x6A, 0x83, 0xFB, 0xC4, 0xA7, 0x70, 0x3D, 0x6F, 0xB3, 0xCC, 0xB6,
|
|
||||||
0x1A, 0xE4, 0x5F, 0x60, 0xD4, 0x31, 0xBA, 0x95, 0x2F, 0x92, 0xF4, 0x81, 0x7B, 0x18, 0x5B, 0x17,
|
|
||||||
0x54, 0x26, 0x70, 0x49, 0xD5, 0x87, 0x34, 0xB9, 0xD3, 0x9C, 0x2F, 0x39, 0xC3, 0xB7, 0x3C, 0xA8,
|
|
||||||
0x03, 0xE4, 0x37, 0x9C, 0x72, 0x39, 0xB0, 0xBF, 0x07, 0x5D, 0x33, 0x2A, 0x41, 0x79, 0xB1, 0x26,
|
|
||||||
0x9B, 0xE6, 0x7C, 0x02, 0x82, 0x01, 0x70, 0xB1, 0xA3, 0x48, 0xCD, 0x2B, 0xCB, 0x98, 0x9B, 0x57,
|
|
||||||
0x96, 0x54, 0xE2, 0x5F, 0x59, 0xCC, 0xDB, 0x9F, 0xFC, 0xDB, 0x4C, 0xF9, 0x7F, 0x5B, 0x28, 0x36,
|
|
||||||
0x32, 0xF9, 0xE1, 0x09, 0xF7, 0x56, 0x3F, 0x45, 0xAD, 0x47, 0x51, 0xBB, 0xF7, 0xFF, 0x17, 0x53,
|
|
||||||
0xE8, 0x9D, 0x36, 0x92, 0x29, 0x00, 0x00
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SPIFFS_MAXLENGTH_FILEPATH 32
|
|
||||||
const char *excludeListFile = "/.exclude.files";
|
|
||||||
|
|
||||||
typedef struct ExcludeListS {
|
|
||||||
char *item;
|
|
||||||
ExcludeListS *next;
|
|
||||||
} ExcludeList;
|
|
||||||
|
|
||||||
static ExcludeList *excludes = NULL;
|
|
||||||
|
|
||||||
static bool matchWild(const char *pattern, const char *testee) {
|
|
||||||
const char *nxPat = NULL, *nxTst = NULL;
|
|
||||||
|
|
||||||
while (*testee) {
|
|
||||||
if (( *pattern == '?' ) || (*pattern == *testee)){
|
|
||||||
pattern++;testee++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (*pattern=='*'){
|
|
||||||
nxPat=pattern++; nxTst=testee;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (nxPat){
|
|
||||||
pattern = nxPat+1; testee=++nxTst;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
while (*pattern=='*'){pattern++;}
|
|
||||||
return (*pattern == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool addExclude(const char *item){
|
|
||||||
size_t len = strlen(item);
|
|
||||||
if(!len){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ExcludeList *e = (ExcludeList *)malloc(sizeof(ExcludeList));
|
|
||||||
if(!e){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
e->item = (char *)malloc(len+1);
|
|
||||||
if(!e->item){
|
|
||||||
free(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
memcpy(e->item, item, len+1);
|
|
||||||
e->next = excludes;
|
|
||||||
excludes = e;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void loadExcludeList(fs::FS &_fs, const char *filename){
|
|
||||||
static char linebuf[SPIFFS_MAXLENGTH_FILEPATH];
|
|
||||||
fs::File excludeFile=_fs.open(filename, "r");
|
|
||||||
if(!excludeFile){
|
|
||||||
//addExclude("/*.js.gz");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#ifdef ESP32
|
|
||||||
if(excludeFile.isDirectory()){
|
|
||||||
excludeFile.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (excludeFile.size() > 0){
|
|
||||||
uint8_t idx;
|
|
||||||
bool isOverflowed = false;
|
|
||||||
while (excludeFile.available()){
|
|
||||||
linebuf[0] = '\0';
|
|
||||||
idx = 0;
|
|
||||||
int lastChar;
|
|
||||||
do {
|
|
||||||
lastChar = excludeFile.read();
|
|
||||||
if(lastChar != '\r'){
|
|
||||||
linebuf[idx++] = (char) lastChar;
|
|
||||||
}
|
|
||||||
} while ((lastChar >= 0) && (lastChar != '\n') && (idx < SPIFFS_MAXLENGTH_FILEPATH));
|
|
||||||
|
|
||||||
if(isOverflowed){
|
|
||||||
isOverflowed = (lastChar != '\n');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
isOverflowed = (idx >= SPIFFS_MAXLENGTH_FILEPATH);
|
|
||||||
linebuf[idx-1] = '\0';
|
|
||||||
if(!addExclude(linebuf)){
|
|
||||||
excludeFile.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
excludeFile.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isExcluded(fs::FS &_fs, const char *filename) {
|
|
||||||
if(excludes == NULL){
|
|
||||||
loadExcludeList(_fs, excludeListFile);
|
|
||||||
}
|
|
||||||
ExcludeList *e = excludes;
|
|
||||||
while(e){
|
|
||||||
if (matchWild(e->item, filename)){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
e = e->next;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// WEB HANDLER IMPLEMENTATION
|
|
||||||
|
|
||||||
#ifdef ESP32
|
|
||||||
SPIFFSEditor::SPIFFSEditor(const fs::FS& fs, const String& username, const String& password)
|
|
||||||
#else
|
|
||||||
SPIFFSEditor::SPIFFSEditor(const String& username, const String& password, const fs::FS& fs)
|
|
||||||
#endif
|
|
||||||
:_fs(fs)
|
|
||||||
,_username(username)
|
|
||||||
,_password(password)
|
|
||||||
,_authenticated(false)
|
|
||||||
,_startTime(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool SPIFFSEditor::canHandle(AsyncWebServerRequest *request){
|
|
||||||
if(request->url().equalsIgnoreCase("/edit")){
|
|
||||||
if(request->method() == HTTP_GET){
|
|
||||||
if(request->hasParam("list"))
|
|
||||||
return true;
|
|
||||||
if(request->hasParam("edit")){
|
|
||||||
request->_tempFile = _fs.open(request->arg("edit"), "r");
|
|
||||||
if(!request->_tempFile){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#ifdef ESP32
|
|
||||||
if(request->_tempFile.isDirectory()){
|
|
||||||
request->_tempFile.close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
if(request->hasParam("download")){
|
|
||||||
request->_tempFile = _fs.open(request->arg("download"), "r");
|
|
||||||
if(!request->_tempFile){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#ifdef ESP32
|
|
||||||
if(request->_tempFile.isDirectory()){
|
|
||||||
request->_tempFile.close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
request->addInterestingHeader("If-Modified-Since");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if(request->method() == HTTP_POST)
|
|
||||||
return true;
|
|
||||||
else if(request->method() == HTTP_DELETE)
|
|
||||||
return true;
|
|
||||||
else if(request->method() == HTTP_PUT)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request){
|
|
||||||
if(_username.length() && _password.length() && !request->authenticate(_username.c_str(), _password.c_str()))
|
|
||||||
return request->requestAuthentication();
|
|
||||||
|
|
||||||
if(request->method() == HTTP_GET){
|
|
||||||
if(request->hasParam("list")){
|
|
||||||
String path = request->getParam("list")->value();
|
|
||||||
#ifdef ESP32
|
|
||||||
File dir = _fs.open(path);
|
|
||||||
#else
|
|
||||||
Dir dir = _fs.openDir(path);
|
|
||||||
#endif
|
|
||||||
path = String();
|
|
||||||
String output = "[";
|
|
||||||
#ifdef ESP32
|
|
||||||
File entry = dir.openNextFile();
|
|
||||||
while(entry){
|
|
||||||
#else
|
|
||||||
while(dir.next()){
|
|
||||||
fs::File entry = dir.openFile("r");
|
|
||||||
#endif
|
|
||||||
if (isExcluded(_fs, entry.name())) {
|
|
||||||
#ifdef ESP32
|
|
||||||
entry = dir.openNextFile();
|
|
||||||
#endif
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (output != "[") output += ',';
|
|
||||||
output += "{\"type\":\"";
|
|
||||||
output += "file";
|
|
||||||
output += "\",\"name\":\"";
|
|
||||||
output += String(entry.name());
|
|
||||||
output += "\",\"size\":";
|
|
||||||
output += String(entry.size());
|
|
||||||
output += "}";
|
|
||||||
#ifdef ESP32
|
|
||||||
entry = dir.openNextFile();
|
|
||||||
#else
|
|
||||||
entry.close();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef ESP32
|
|
||||||
dir.close();
|
|
||||||
#endif
|
|
||||||
output += "]";
|
|
||||||
request->send(200, "application/json", output);
|
|
||||||
output = String();
|
|
||||||
}
|
|
||||||
else if(request->hasParam("edit") || request->hasParam("download")){
|
|
||||||
request->send(request->_tempFile, request->_tempFile.name(), String(), request->hasParam("download"));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const char * buildTime = __DATE__ " " __TIME__ " GMT";
|
|
||||||
if (request->header("If-Modified-Since").equals(buildTime)) {
|
|
||||||
request->send(304);
|
|
||||||
} else {
|
|
||||||
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", edit_htm_gz, edit_htm_gz_len);
|
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
|
||||||
response->addHeader("Last-Modified", buildTime);
|
|
||||||
request->send(response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if(request->method() == HTTP_DELETE){
|
|
||||||
if(request->hasParam("path", true)){
|
|
||||||
_fs.remove(request->getParam("path", true)->value());
|
|
||||||
request->send(200, "", "DELETE: "+request->getParam("path", true)->value());
|
|
||||||
} else
|
|
||||||
request->send(404);
|
|
||||||
} else if(request->method() == HTTP_POST){
|
|
||||||
if(request->hasParam("data", true, true) && _fs.exists(request->getParam("data", true, true)->value()))
|
|
||||||
request->send(200, "", "UPLOADED: "+request->getParam("data", true, true)->value());
|
|
||||||
else
|
|
||||||
request->send(500);
|
|
||||||
} else if(request->method() == HTTP_PUT){
|
|
||||||
if(request->hasParam("path", true)){
|
|
||||||
String filename = request->getParam("path", true)->value();
|
|
||||||
if(_fs.exists(filename)){
|
|
||||||
request->send(200);
|
|
||||||
} else {
|
|
||||||
fs::File f = _fs.open(filename, "w");
|
|
||||||
if(f){
|
|
||||||
f.write((uint8_t)0x00);
|
|
||||||
f.close();
|
|
||||||
request->send(200, "", "CREATE: "+filename);
|
|
||||||
} else {
|
|
||||||
request->send(500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
request->send(400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SPIFFSEditor::handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){
|
|
||||||
if(!index){
|
|
||||||
if(!_username.length() || request->authenticate(_username.c_str(),_password.c_str())){
|
|
||||||
_authenticated = true;
|
|
||||||
request->_tempFile = _fs.open(filename, "w");
|
|
||||||
_startTime = millis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(_authenticated && request->_tempFile){
|
|
||||||
if(len){
|
|
||||||
request->_tempFile.write(data,len);
|
|
||||||
}
|
|
||||||
if(final){
|
|
||||||
request->_tempFile.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
#ifndef SPIFFSEditor_H_
|
|
||||||
#define SPIFFSEditor_H_
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
|
|
||||||
class SPIFFSEditor : public AsyncWebHandler {
|
|
||||||
private:
|
|
||||||
fs::FS _fs;
|
|
||||||
String _username;
|
|
||||||
String _password;
|
|
||||||
bool _authenticated;
|
|
||||||
uint32_t _startTime;
|
|
||||||
|
|
||||||
public:
|
|
||||||
#ifdef ESP32
|
|
||||||
SPIFFSEditor(const fs::FS & fs, const String & username = String(), const String & password = String());
|
|
||||||
#else
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
||||||
SPIFFSEditor(const String & username = String(), const String & password = String(), const fs::FS & fs = SPIFFS);
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
#endif
|
|
||||||
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
|
|
||||||
@@ -1,789 +0,0 @@
|
|||||||
/*
|
|
||||||
Asynchronous WebServer library for Espressif MCUs
|
|
||||||
|
|
||||||
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
|
|
||||||
This file is part of the esp8266 core for Arduino environment.
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
#include "ESPAsyncWebServer.h"
|
|
||||||
#include "WebResponseImpl.h"
|
|
||||||
#include "cbuf.h"
|
|
||||||
|
|
||||||
// Since ESP8266 does not link memchr by default, here's its implementation.
|
|
||||||
void * memchr(void * ptr, int ch, size_t count) {
|
|
||||||
unsigned char * p = static_cast<unsigned char *>(ptr);
|
|
||||||
while (count--)
|
|
||||||
if (*p++ == static_cast<unsigned char>(ch))
|
|
||||||
return --p;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Abstract Response
|
|
||||||
* */
|
|
||||||
const char * AsyncWebServerResponse::_responseCodeToString(int code) {
|
|
||||||
switch (code) {
|
|
||||||
case 100:
|
|
||||||
return ("Continue");
|
|
||||||
case 101:
|
|
||||||
return ("Switching Protocols");
|
|
||||||
case 200:
|
|
||||||
return ("OK");
|
|
||||||
case 201:
|
|
||||||
return ("Created");
|
|
||||||
case 202:
|
|
||||||
return ("Accepted"); // proddy: used in wifi
|
|
||||||
case 203:
|
|
||||||
return ("Non-Authoritative Information");
|
|
||||||
case 204:
|
|
||||||
return ("No Content");
|
|
||||||
case 205:
|
|
||||||
return ("Reset Content"); // proddy: reboot required
|
|
||||||
case 206:
|
|
||||||
return ("Partial Content");
|
|
||||||
case 300:
|
|
||||||
return ("Multiple Choices");
|
|
||||||
case 301:
|
|
||||||
return ("Moved Permanently");
|
|
||||||
case 302:
|
|
||||||
return ("Found");
|
|
||||||
case 303:
|
|
||||||
return ("See Other");
|
|
||||||
case 304:
|
|
||||||
return ("Not Modified");
|
|
||||||
case 305:
|
|
||||||
return ("Use Proxy");
|
|
||||||
case 307:
|
|
||||||
return ("Temporary Redirect");
|
|
||||||
case 400:
|
|
||||||
return ("Bad Request");
|
|
||||||
case 401:
|
|
||||||
return ("Unauthorized");
|
|
||||||
case 402:
|
|
||||||
return ("Payment Required");
|
|
||||||
case 403:
|
|
||||||
return ("Forbidden");
|
|
||||||
case 404:
|
|
||||||
return ("Not Found");
|
|
||||||
case 405:
|
|
||||||
return ("Method Not Allowed");
|
|
||||||
case 406:
|
|
||||||
return ("Not Acceptable");
|
|
||||||
case 407:
|
|
||||||
return ("Proxy Authentication Required");
|
|
||||||
case 408:
|
|
||||||
return ("Request Time-out");
|
|
||||||
case 409:
|
|
||||||
return ("Conflict");
|
|
||||||
case 410:
|
|
||||||
return ("Gone");
|
|
||||||
case 411:
|
|
||||||
return ("Length Required");
|
|
||||||
case 412:
|
|
||||||
return ("Precondition Failed");
|
|
||||||
case 413:
|
|
||||||
return ("Request Entity Too Large");
|
|
||||||
case 414:
|
|
||||||
return ("Request-URI Too Large");
|
|
||||||
case 415:
|
|
||||||
return ("Unsupported Media Type");
|
|
||||||
case 416:
|
|
||||||
return ("Requested range not satisfiable");
|
|
||||||
case 417:
|
|
||||||
return ("Expectation Failed");
|
|
||||||
case 500:
|
|
||||||
return ("Internal Server Error");
|
|
||||||
case 501:
|
|
||||||
return ("Not Implemented");
|
|
||||||
case 502:
|
|
||||||
return ("Bad Gateway");
|
|
||||||
case 503:
|
|
||||||
return ("Service Unavailable");
|
|
||||||
case 504:
|
|
||||||
return ("Gateway Time-out");
|
|
||||||
case 505:
|
|
||||||
return ("HTTP Version not supported");
|
|
||||||
case 507:
|
|
||||||
return ("Insufficient Storage");
|
|
||||||
default:
|
|
||||||
return ("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const __FlashStringHelper * AsyncWebServerResponse::responseCodeToString(int code) {
|
|
||||||
return reinterpret_cast<const __FlashStringHelper *>(responseCodeToString(code));
|
|
||||||
}
|
|
||||||
|
|
||||||
AsyncWebServerResponse::AsyncWebServerResponse()
|
|
||||||
: _code(0)
|
|
||||||
, _headers(LinkedList<AsyncWebHeader *>([](AsyncWebHeader * h) { delete h; }))
|
|
||||||
, _contentType()
|
|
||||||
, _contentLength(0)
|
|
||||||
, _sendContentLength(true)
|
|
||||||
, _chunked(false)
|
|
||||||
, _headLength(0)
|
|
||||||
, _sentLength(0)
|
|
||||||
, _ackedLength(0)
|
|
||||||
, _writtenLength(0)
|
|
||||||
, _state(RESPONSE_SETUP) {
|
|
||||||
for (auto header : DefaultHeaders::Instance()) {
|
|
||||||
_headers.add(new AsyncWebHeader(header->name(), header->value()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AsyncWebServerResponse::~AsyncWebServerResponse() {
|
|
||||||
_headers.free();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncWebServerResponse::setCode(int code) {
|
|
||||||
if (_state == RESPONSE_SETUP)
|
|
||||||
_code = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncWebServerResponse::setContentLength(size_t len) {
|
|
||||||
if (_state == RESPONSE_SETUP)
|
|
||||||
_contentLength = len;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncWebServerResponse::setContentType(const String & type) {
|
|
||||||
if (_state == RESPONSE_SETUP)
|
|
||||||
_contentType = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncWebServerResponse::addHeader(const String & name, const String & value) {
|
|
||||||
_headers.add(new AsyncWebHeader(name, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
String AsyncWebServerResponse::_assembleHead(uint8_t version) {
|
|
||||||
if (version) {
|
|
||||||
addHeader(F("Accept-Ranges"), F("none"));
|
|
||||||
if (_chunked)
|
|
||||||
addHeader(F("Transfer-Encoding"), F("chunked"));
|
|
||||||
}
|
|
||||||
String out = String();
|
|
||||||
int bufSize = 300;
|
|
||||||
char buf[bufSize];
|
|
||||||
|
|
||||||
snprintf_P(buf, bufSize, PSTR("HTTP/1.%d %d %s\r\n"), version, _code, _responseCodeToString(_code));
|
|
||||||
out.concat(buf);
|
|
||||||
|
|
||||||
if (_sendContentLength) {
|
|
||||||
snprintf_P(buf, bufSize, PSTR("Content-Length: %d\r\n"), _contentLength);
|
|
||||||
out.concat(buf);
|
|
||||||
}
|
|
||||||
if (_contentType.length()) {
|
|
||||||
snprintf_P(buf, bufSize, PSTR("Content-Type: %s\r\n"), _contentType.c_str());
|
|
||||||
out.concat(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto & header : _headers) {
|
|
||||||
snprintf_P(buf, bufSize, PSTR("%s: %s\r\n"), header->name().c_str(), header->value().c_str());
|
|
||||||
out.concat(buf);
|
|
||||||
}
|
|
||||||
_headers.free();
|
|
||||||
|
|
||||||
out.concat(F("\r\n"));
|
|
||||||
_headLength = out.length();
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AsyncWebServerResponse::_started() const {
|
|
||||||
return _state > RESPONSE_SETUP;
|
|
||||||
}
|
|
||||||
bool AsyncWebServerResponse::_finished() const {
|
|
||||||
return _state > RESPONSE_WAIT_ACK;
|
|
||||||
}
|
|
||||||
bool AsyncWebServerResponse::_failed() const {
|
|
||||||
return _state == RESPONSE_FAILED;
|
|
||||||
}
|
|
||||||
bool AsyncWebServerResponse::_sourceValid() const {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
void AsyncWebServerResponse::_respond(AsyncWebServerRequest * request) {
|
|
||||||
_state = RESPONSE_END;
|
|
||||||
request->client()->close();
|
|
||||||
}
|
|
||||||
size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest * request, size_t len, uint32_t time) {
|
|
||||||
(void)request;
|
|
||||||
(void)len;
|
|
||||||
(void)time;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* String/Code Response
|
|
||||||
* */
|
|
||||||
AsyncBasicResponse::AsyncBasicResponse(int code, const String & contentType, const String & content) {
|
|
||||||
_code = code;
|
|
||||||
_content = content;
|
|
||||||
_contentType = contentType;
|
|
||||||
if (_content.length()) {
|
|
||||||
_contentLength = _content.length();
|
|
||||||
if (!_contentType.length())
|
|
||||||
_contentType = F("text/plain");
|
|
||||||
}
|
|
||||||
addHeader(F("Connection"), F("close"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncBasicResponse::_respond(AsyncWebServerRequest * request) {
|
|
||||||
_state = RESPONSE_HEADERS;
|
|
||||||
String out = _assembleHead(request->version());
|
|
||||||
size_t outLen = out.length();
|
|
||||||
size_t space = request->client()->space();
|
|
||||||
if (!_contentLength && space >= outLen) {
|
|
||||||
_writtenLength += request->client()->write(out.c_str(), outLen);
|
|
||||||
_state = RESPONSE_WAIT_ACK;
|
|
||||||
} else if (_contentLength && space >= outLen + _contentLength) {
|
|
||||||
out += _content;
|
|
||||||
outLen += _contentLength;
|
|
||||||
_writtenLength += request->client()->write(out.c_str(), outLen);
|
|
||||||
_state = RESPONSE_WAIT_ACK;
|
|
||||||
} else if (space && space < outLen) {
|
|
||||||
String partial = out.substring(0, space);
|
|
||||||
_content = out.substring(space) + _content;
|
|
||||||
_contentLength += outLen - space;
|
|
||||||
_writtenLength += request->client()->write(partial.c_str(), partial.length());
|
|
||||||
_state = RESPONSE_CONTENT;
|
|
||||||
} else if (space > outLen && space < (outLen + _contentLength)) {
|
|
||||||
size_t shift = space - outLen;
|
|
||||||
outLen += shift;
|
|
||||||
_sentLength += shift;
|
|
||||||
out += _content.substring(0, shift);
|
|
||||||
_content = _content.substring(shift);
|
|
||||||
_writtenLength += request->client()->write(out.c_str(), outLen);
|
|
||||||
_state = RESPONSE_CONTENT;
|
|
||||||
} else {
|
|
||||||
_content = out + _content;
|
|
||||||
_contentLength += outLen;
|
|
||||||
_state = RESPONSE_CONTENT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AsyncBasicResponse::_ack(AsyncWebServerRequest * request, size_t len, uint32_t time) {
|
|
||||||
(void)time;
|
|
||||||
_ackedLength += len;
|
|
||||||
if (_state == RESPONSE_CONTENT) {
|
|
||||||
size_t available = _contentLength - _sentLength;
|
|
||||||
size_t space = request->client()->space();
|
|
||||||
//we can fit in this packet
|
|
||||||
if (space > available) {
|
|
||||||
_writtenLength += request->client()->write(_content.c_str(), available);
|
|
||||||
_content = String();
|
|
||||||
_state = RESPONSE_WAIT_ACK;
|
|
||||||
return available;
|
|
||||||
}
|
|
||||||
//send some data, the rest on ack
|
|
||||||
String out = _content.substring(0, space);
|
|
||||||
_content = _content.substring(space);
|
|
||||||
_sentLength += space;
|
|
||||||
_writtenLength += request->client()->write(out.c_str(), space);
|
|
||||||
return space;
|
|
||||||
} else if (_state == RESPONSE_WAIT_ACK) {
|
|
||||||
if (_ackedLength >= _writtenLength) {
|
|
||||||
_state = RESPONSE_END;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Abstract Response
|
|
||||||
* */
|
|
||||||
|
|
||||||
AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback)
|
|
||||||
: _callback(callback) {
|
|
||||||
// In case of template processing, we're unable to determine real response size
|
|
||||||
if (callback) {
|
|
||||||
_contentLength = 0;
|
|
||||||
_sendContentLength = false;
|
|
||||||
_chunked = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncAbstractResponse::_respond(AsyncWebServerRequest * request) {
|
|
||||||
addHeader(F("Connection"), F("close"));
|
|
||||||
_head = _assembleHead(request->version());
|
|
||||||
_state = RESPONSE_HEADERS;
|
|
||||||
_ack(request, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest * request, size_t len, uint32_t time) {
|
|
||||||
(void)time;
|
|
||||||
if (!_sourceValid()) {
|
|
||||||
_state = RESPONSE_FAILED;
|
|
||||||
request->client()->close();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
_ackedLength += len;
|
|
||||||
size_t space = request->client()->space();
|
|
||||||
|
|
||||||
size_t headLen = _head.length();
|
|
||||||
if (_state == RESPONSE_HEADERS) {
|
|
||||||
if (space >= headLen) {
|
|
||||||
_state = RESPONSE_CONTENT;
|
|
||||||
space -= headLen;
|
|
||||||
} else {
|
|
||||||
String out = _head.substring(0, space);
|
|
||||||
_head = _head.substring(space);
|
|
||||||
_writtenLength += request->client()->write(out.c_str(), out.length());
|
|
||||||
return out.length();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_state == RESPONSE_CONTENT) {
|
|
||||||
size_t outLen;
|
|
||||||
if (_chunked) {
|
|
||||||
if (space <= 8) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
outLen = space;
|
|
||||||
} else if (!_sendContentLength) {
|
|
||||||
outLen = space;
|
|
||||||
} else {
|
|
||||||
outLen = ((_contentLength - _sentLength) > space) ? space : (_contentLength - _sentLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t * buf = (uint8_t *)malloc(outLen + headLen);
|
|
||||||
if (!buf) {
|
|
||||||
// os_printf("_ack malloc %d failed\n", outLen+headLen);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (headLen) {
|
|
||||||
memcpy(buf, _head.c_str(), _head.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t readLen = 0;
|
|
||||||
|
|
||||||
if (_chunked) {
|
|
||||||
// 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 = snprintf_P((char *)buf + headLen, sizeof(buf) - headLen - 2, PSTR("%x"), readLen) + headLen;
|
|
||||||
while (outLen < headLen + 4)
|
|
||||||
buf[outLen++] = ' ';
|
|
||||||
buf[outLen++] = '\r';
|
|
||||||
buf[outLen++] = '\n';
|
|
||||||
outLen += readLen;
|
|
||||||
buf[outLen++] = '\r';
|
|
||||||
buf[outLen++] = '\n';
|
|
||||||
} else {
|
|
||||||
readLen = _fillBufferAndProcessTemplates(buf + headLen, outLen);
|
|
||||||
if (readLen == RESPONSE_TRY_AGAIN) {
|
|
||||||
free(buf);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
outLen = readLen + headLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (headLen) {
|
|
||||||
_head = String();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outLen) {
|
|
||||||
_writtenLength += request->client()->write((const char *)buf, outLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_chunked) {
|
|
||||||
_sentLength += readLen;
|
|
||||||
} else {
|
|
||||||
_sentLength += outLen - headLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(buf);
|
|
||||||
|
|
||||||
if ((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)) {
|
|
||||||
_state = RESPONSE_WAIT_ACK;
|
|
||||||
}
|
|
||||||
return outLen;
|
|
||||||
|
|
||||||
} else if (_state == RESPONSE_WAIT_ACK) {
|
|
||||||
if (!_sendContentLength || _ackedLength >= _writtenLength) {
|
|
||||||
_state = RESPONSE_END;
|
|
||||||
if (!_chunked && !_sendContentLength)
|
|
||||||
request->client()->close(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t * data, const size_t len) {
|
|
||||||
// If we have something in cache, copy it to buffer
|
|
||||||
const size_t readFromCache = std::min(len, _cache.size());
|
|
||||||
if (readFromCache) {
|
|
||||||
memcpy(data, _cache.data(), readFromCache);
|
|
||||||
_cache.erase(_cache.begin(), _cache.begin() + readFromCache);
|
|
||||||
}
|
|
||||||
// If we need to read more...
|
|
||||||
const size_t needFromFile = len - readFromCache;
|
|
||||||
const size_t readFromContent = _fillBuffer(data + readFromCache, needFromFile);
|
|
||||||
return readFromCache + readFromContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t * data, size_t len) {
|
|
||||||
if (!_callback)
|
|
||||||
return _fillBuffer(data, len);
|
|
||||||
|
|
||||||
const size_t originalLen = len;
|
|
||||||
len = _readDataFromCacheOrContent(data, len);
|
|
||||||
// Now we've read 'len' bytes, either from cache or from file
|
|
||||||
// Search for template placeholders
|
|
||||||
uint8_t * pTemplateStart = data;
|
|
||||||
while ((pTemplateStart < &data[len])
|
|
||||||
&& (pTemplateStart = (uint8_t *)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1]
|
|
||||||
uint8_t * pTemplateEnd =
|
|
||||||
(pTemplateStart < &data[len - 1]) ? (uint8_t *)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr;
|
|
||||||
// temporary buffer to hold parameter name
|
|
||||||
uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1];
|
|
||||||
String paramName;
|
|
||||||
// If closing placeholder is found:
|
|
||||||
if (pTemplateEnd) {
|
|
||||||
// prepare argument to callback
|
|
||||||
const size_t paramNameLength = std::min((size_t)sizeof(buf) - 1, (size_t)(pTemplateEnd - pTemplateStart - 1));
|
|
||||||
if (paramNameLength) {
|
|
||||||
memcpy(buf, pTemplateStart + 1, paramNameLength);
|
|
||||||
buf[paramNameLength] = 0;
|
|
||||||
paramName = String(reinterpret_cast<char *>(buf));
|
|
||||||
} else { // double percent sign encountered, this is single percent sign escaped.
|
|
||||||
// remove the 2nd percent sign
|
|
||||||
memmove(pTemplateEnd, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
|
|
||||||
len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1;
|
|
||||||
++pTemplateStart;
|
|
||||||
}
|
|
||||||
} else if (&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data
|
|
||||||
memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart);
|
|
||||||
const size_t readFromCacheOrContent =
|
|
||||||
_readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1));
|
|
||||||
if (readFromCacheOrContent) {
|
|
||||||
pTemplateEnd = (uint8_t *)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent);
|
|
||||||
if (pTemplateEnd) {
|
|
||||||
// prepare argument to callback
|
|
||||||
*pTemplateEnd = 0;
|
|
||||||
paramName = String(reinterpret_cast<char *>(buf));
|
|
||||||
// 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.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
|
|
||||||
++pTemplateStart;
|
|
||||||
}
|
|
||||||
} else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
|
|
||||||
++pTemplateStart;
|
|
||||||
} else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
|
|
||||||
++pTemplateStart;
|
|
||||||
if (paramName.length()) {
|
|
||||||
// call callback and replace with result.
|
|
||||||
// Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value.
|
|
||||||
// Data after pTemplateEnd may need to be moved.
|
|
||||||
// The first byte of data after placeholder is located at pTemplateEnd + 1.
|
|
||||||
// It should be located at pTemplateStart + numBytesCopied (to begin right after inserted parameter value).
|
|
||||||
const String paramValue(_callback(paramName));
|
|
||||||
const char * pvstr = paramValue.c_str();
|
|
||||||
const unsigned int pvlen = paramValue.length();
|
|
||||||
const size_t numBytesCopied = std::min(pvlen, static_cast<unsigned int>(&data[originalLen - 1] - pTemplateStart + 1));
|
|
||||||
// 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)) {
|
|
||||||
_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);
|
|
||||||
len = originalLen; // fix issue with truncated data, not sure if it has any side effects
|
|
||||||
} else if (pTemplateEnd + 1 != pTemplateStart + numBytesCopied)
|
|
||||||
//2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit.
|
|
||||||
// Move the entire data after the placeholder
|
|
||||||
memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
|
|
||||||
// 3. replace placeholder with actual value
|
|
||||||
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(_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;
|
|
||||||
const size_t totalFreeRoom = originalLen - len + roomFreed;
|
|
||||||
len += _readDataFromCacheOrContent(&data[len - roomFreed], totalFreeRoom) - roomFreed;
|
|
||||||
} else { // result is copied fully; it is longer than placeholder text
|
|
||||||
const size_t roomTaken = pTemplateStart + numBytesCopied - pTemplateEnd - 1;
|
|
||||||
len = std::min(len + roomTaken, originalLen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // while(pTemplateStart)
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* File Response
|
|
||||||
* */
|
|
||||||
|
|
||||||
AsyncFileResponse::~AsyncFileResponse() {
|
|
||||||
if (_content)
|
|
||||||
_content.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncFileResponse::_setContentType(const String & path) {
|
|
||||||
#if HAVE_EXTERN_GET_CONTENT_TYPE_FUNCTION
|
|
||||||
extern const __FlashStringHelper * getContentType(const String & path);
|
|
||||||
_contentType = getContentType(path);
|
|
||||||
#else
|
|
||||||
if (path.endsWith(F(".html")))
|
|
||||||
_contentType = F("text/html");
|
|
||||||
else if (path.endsWith(F(".htm")))
|
|
||||||
_contentType = F("text/html");
|
|
||||||
else if (path.endsWith(F(".css")))
|
|
||||||
_contentType = F("text/css");
|
|
||||||
else if (path.endsWith(F(".json")))
|
|
||||||
_contentType = F("application/json");
|
|
||||||
else if (path.endsWith(F(".js")))
|
|
||||||
_contentType = F("application/javascript");
|
|
||||||
else if (path.endsWith(F(".png")))
|
|
||||||
_contentType = F("image/png");
|
|
||||||
else if (path.endsWith(F(".gif")))
|
|
||||||
_contentType = F("image/gif");
|
|
||||||
else if (path.endsWith(F(".jpg")))
|
|
||||||
_contentType = F("image/jpeg");
|
|
||||||
else if (path.endsWith(F(".ico")))
|
|
||||||
_contentType = F("image/x-icon");
|
|
||||||
else if (path.endsWith(F(".svg")))
|
|
||||||
_contentType = F("image/svg+xml");
|
|
||||||
else if (path.endsWith(F(".eot")))
|
|
||||||
_contentType = F("font/eot");
|
|
||||||
else if (path.endsWith(F(".woff")))
|
|
||||||
_contentType = F("font/woff");
|
|
||||||
else if (path.endsWith(F(".woff2")))
|
|
||||||
_contentType = F("font/woff2");
|
|
||||||
else if (path.endsWith(F(".ttf")))
|
|
||||||
_contentType = F("font/ttf");
|
|
||||||
else if (path.endsWith(F(".xml")))
|
|
||||||
_contentType = F("text/xml");
|
|
||||||
else if (path.endsWith(F(".pdf")))
|
|
||||||
_contentType = F("application/pdf");
|
|
||||||
else if (path.endsWith(F(".zip")))
|
|
||||||
_contentType = F("application/zip");
|
|
||||||
else if (path.endsWith(F(".gz")))
|
|
||||||
_contentType = F("application/x-gzip");
|
|
||||||
else
|
|
||||||
_contentType = F("text/plain");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
AsyncFileResponse::AsyncFileResponse(FS & fs, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback)
|
|
||||||
: AsyncAbstractResponse(callback) {
|
|
||||||
_code = 200;
|
|
||||||
_path = path;
|
|
||||||
|
|
||||||
if (!download && !fs.exists(_path) && fs.exists(_path + F(".gz"))) {
|
|
||||||
_path = _path + F(".gz");
|
|
||||||
addHeader(F("Content-Encoding"), F("gzip"));
|
|
||||||
_callback = nullptr; // Unable to process zipped templates
|
|
||||||
_sendContentLength = true;
|
|
||||||
_chunked = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_content = fs.open(_path, fs::FileOpenMode::read);
|
|
||||||
_contentLength = _content.size();
|
|
||||||
|
|
||||||
if (contentType.length() == 0)
|
|
||||||
_setContentType(path);
|
|
||||||
else
|
|
||||||
_contentType = contentType;
|
|
||||||
|
|
||||||
int filenameStart = path.lastIndexOf('/') + 1;
|
|
||||||
char buf[26 + path.length() - filenameStart];
|
|
||||||
char * filename = (char *)path.c_str() + filenameStart;
|
|
||||||
|
|
||||||
if (download) {
|
|
||||||
// set filename and force download
|
|
||||||
snprintf_P(buf, sizeof(buf), PSTR("attachment; filename=\"%s\""), filename);
|
|
||||||
} else {
|
|
||||||
// set filename and force rendering
|
|
||||||
snprintf_P(buf, sizeof(buf), PSTR("inline; filename=\"%s\""), filename);
|
|
||||||
}
|
|
||||||
addHeader(F("Content-Disposition"), buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
AsyncFileResponse::AsyncFileResponse(File content, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback)
|
|
||||||
: AsyncAbstractResponse(callback) {
|
|
||||||
_code = 200;
|
|
||||||
_path = path;
|
|
||||||
|
|
||||||
if (!download && String(content.name()).endsWith(F(".gz")) && !path.endsWith(F(".gz"))) {
|
|
||||||
addHeader(F("Content-Encoding"), F("gzip"));
|
|
||||||
_callback = nullptr; // Unable to process gzipped templates
|
|
||||||
_sendContentLength = true;
|
|
||||||
_chunked = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_content = content;
|
|
||||||
_contentLength = _content.size();
|
|
||||||
|
|
||||||
if (contentType.length() == 0)
|
|
||||||
_setContentType(path);
|
|
||||||
else
|
|
||||||
_contentType = contentType;
|
|
||||||
|
|
||||||
int filenameStart = path.lastIndexOf('/') + 1;
|
|
||||||
char buf[26 + path.length() - filenameStart];
|
|
||||||
char * filename = (char *)path.c_str() + filenameStart;
|
|
||||||
|
|
||||||
if (download) {
|
|
||||||
snprintf_P(buf, sizeof(buf), PSTR("attachment; filename=\"%s\""), filename);
|
|
||||||
} else {
|
|
||||||
snprintf_P(buf, sizeof(buf), PSTR("inline; filename=\"%s\""), filename);
|
|
||||||
}
|
|
||||||
addHeader(F("Content-Disposition"), buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AsyncFileResponse::_fillBuffer(uint8_t * data, size_t len) {
|
|
||||||
return _content.read(data, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Stream Response
|
|
||||||
* */
|
|
||||||
|
|
||||||
AsyncStreamResponse::AsyncStreamResponse(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback)
|
|
||||||
: AsyncAbstractResponse(callback) {
|
|
||||||
_code = 200;
|
|
||||||
_content = &stream;
|
|
||||||
_contentLength = len;
|
|
||||||
_contentType = contentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AsyncStreamResponse::_fillBuffer(uint8_t * data, size_t len) {
|
|
||||||
size_t available = _content->available();
|
|
||||||
size_t outLen = (available > len) ? len : available;
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < outLen; i++)
|
|
||||||
data[i] = _content->read();
|
|
||||||
return outLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Callback Response
|
|
||||||
* */
|
|
||||||
|
|
||||||
AsyncCallbackResponse::AsyncCallbackResponse(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback)
|
|
||||||
: AsyncAbstractResponse(templateCallback) {
|
|
||||||
_code = 200;
|
|
||||||
_content = callback;
|
|
||||||
_contentLength = len;
|
|
||||||
if (!len)
|
|
||||||
_sendContentLength = false;
|
|
||||||
_contentType = contentType;
|
|
||||||
_filledLength = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Chunked Response
|
|
||||||
* */
|
|
||||||
|
|
||||||
AsyncChunkedResponse::AsyncChunkedResponse(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback)
|
|
||||||
: AsyncAbstractResponse(processorCallback) {
|
|
||||||
_code = 200;
|
|
||||||
_content = callback;
|
|
||||||
_contentLength = 0;
|
|
||||||
_contentType = contentType;
|
|
||||||
_sendContentLength = false;
|
|
||||||
_chunked = true;
|
|
||||||
_filledLength = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Progmem Response
|
|
||||||
* */
|
|
||||||
|
|
||||||
AsyncProgmemResponse::AsyncProgmemResponse(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback)
|
|
||||||
: AsyncAbstractResponse(callback) {
|
|
||||||
_code = code;
|
|
||||||
_content = content;
|
|
||||||
_contentType = contentType;
|
|
||||||
_contentLength = len;
|
|
||||||
_readLength = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AsyncProgmemResponse::_fillBuffer(uint8_t * data, size_t len) {
|
|
||||||
size_t left = _contentLength - _readLength;
|
|
||||||
if (left > len) {
|
|
||||||
memcpy_P(data, _content + _readLength, len);
|
|
||||||
_readLength += len;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
memcpy_P(data, _content + _readLength, left);
|
|
||||||
_readLength += left;
|
|
||||||
return left;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Response Stream (You can print/write/printf to it, up to the contentLen bytes)
|
|
||||||
* */
|
|
||||||
|
|
||||||
AsyncResponseStream::AsyncResponseStream(const String & contentType, size_t bufferSize) {
|
|
||||||
_code = 200;
|
|
||||||
_contentLength = 0;
|
|
||||||
_contentType = contentType;
|
|
||||||
_content = new cbuf(bufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
AsyncResponseStream::~AsyncResponseStream() {
|
|
||||||
delete _content;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AsyncResponseStream::_fillBuffer(uint8_t * buf, size_t maxLen) {
|
|
||||||
return _content->read((char *)buf, maxLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AsyncResponseStream::write(const uint8_t * data, size_t len) {
|
|
||||||
if (_started())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (len > _content->room()) {
|
|
||||||
size_t needed = len - _content->room();
|
|
||||||
_content->resizeAdd(needed);
|
|
||||||
}
|
|
||||||
size_t written = _content->write((const char *)data, len);
|
|
||||||
_contentLength += written;
|
|
||||||
return written;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AsyncResponseStream::write(uint8_t data) {
|
|
||||||
return write(&data, 1);
|
|
||||||
}
|
|
||||||
43
lib/ESPAsyncWebServer/library.json
Normal file
43
lib/ESPAsyncWebServer/library.json
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "ESP Async WebServer",
|
||||||
|
"version": "2.6.1",
|
||||||
|
"description": "Asynchronous HTTP and WebSocket Server Library for ESP32. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.",
|
||||||
|
"keywords": "http,async,websocket,webserver",
|
||||||
|
"homepage": "https://github.com/mathieucarbou/ESPAsyncWebServer",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/mathieucarbou/ESPAsyncWebServer.git"
|
||||||
|
},
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Hristo Gochkov"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Mathieu Carbou",
|
||||||
|
"maintainer": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0",
|
||||||
|
"frameworks": "arduino",
|
||||||
|
"platforms": [
|
||||||
|
"espressif32"
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"owner": "esphome",
|
||||||
|
"name": "AsyncTCP-esphome",
|
||||||
|
"version": "^2.1.1",
|
||||||
|
"platforms": "espressif32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"export": {
|
||||||
|
"include": [
|
||||||
|
"examples",
|
||||||
|
"src",
|
||||||
|
"library.json",
|
||||||
|
"library.properties",
|
||||||
|
"LICENSE",
|
||||||
|
"README.md"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
10
lib/ESPAsyncWebServer/library.properties
Normal file
10
lib/ESPAsyncWebServer/library.properties
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
name=ESP Async WebServer
|
||||||
|
version=2.6.1
|
||||||
|
author=Me-No-Dev
|
||||||
|
maintainer=Mathieu Carbou <mathieu.carbou@gmail.com>
|
||||||
|
sentence=Asynchronous HTTP and WebSocket Server Library for ESP32
|
||||||
|
paragraph=Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc
|
||||||
|
category=Other
|
||||||
|
url=https://github.com/mathieucarbou/ESPAsyncWebServer
|
||||||
|
architectures=esp32
|
||||||
|
license=LGPL-3.0
|
||||||
@@ -137,16 +137,17 @@ size_t AsyncEventSourceMessage::ack(size_t len, uint32_t time) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This could also return void as the return value is not used.
|
||||||
|
// Leaving as-is for compatibility...
|
||||||
size_t AsyncEventSourceMessage::send(AsyncClient *client) {
|
size_t AsyncEventSourceMessage::send(AsyncClient *client) {
|
||||||
const size_t len = _len - _sent;
|
if (_sent >= _len) {
|
||||||
if(client->space() < len){
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
size_t sent = client->add((const char *)_data, len);
|
const size_t len_to_send = _len - _sent;
|
||||||
if(client->canSend())
|
auto position = reinterpret_cast<const char*>(_data + _sent);
|
||||||
client->send();
|
const size_t sent_now = client->write(position, len_to_send);
|
||||||
_sent += sent;
|
_sent += sent_now;
|
||||||
return sent;
|
return sent_now;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client
|
// Client
|
||||||
@@ -173,7 +174,9 @@ AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, A
|
|||||||
}
|
}
|
||||||
|
|
||||||
AsyncEventSourceClient::~AsyncEventSourceClient(){
|
AsyncEventSourceClient::~AsyncEventSourceClient(){
|
||||||
|
_lockmq.lock();
|
||||||
_messageQueue.free();
|
_messageQueue.free();
|
||||||
|
_lockmq.unlock();
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,33 +187,41 @@ void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage)
|
|||||||
delete dataMessage;
|
delete dataMessage;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
//length() is not thread-safe, thus acquiring the lock before this call..
|
||||||
|
_lockmq.lock();
|
||||||
if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){
|
if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){
|
||||||
// ets_printf(String(F("ERROR: Too many messages queued\n")).c_str());
|
ets_printf(String(F("ERROR: Too many messages queued\n")).c_str());
|
||||||
delete dataMessage;
|
delete dataMessage;
|
||||||
} else {
|
} else {
|
||||||
_messageQueue.add(dataMessage);
|
_messageQueue.add(dataMessage);
|
||||||
}
|
// runqueue trigger when new messages added
|
||||||
if(_client->canSend())
|
if(_client->canSend()) {
|
||||||
_runQueue();
|
_runQueue();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
_lockmq.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
void AsyncEventSourceClient::_onAck(size_t len, uint32_t time){
|
void AsyncEventSourceClient::_onAck(size_t len, uint32_t time){
|
||||||
|
// Same here, acquiring the lock early
|
||||||
|
_lockmq.lock();
|
||||||
while(len && !_messageQueue.isEmpty()){
|
while(len && !_messageQueue.isEmpty()){
|
||||||
len = _messageQueue.front()->ack(len, time);
|
len = _messageQueue.front()->ack(len, time);
|
||||||
if(_messageQueue.front()->finished())
|
if(_messageQueue.front()->finished())
|
||||||
_messageQueue.remove(_messageQueue.front());
|
_messageQueue.remove(_messageQueue.front());
|
||||||
}
|
}
|
||||||
|
|
||||||
_runQueue();
|
_runQueue();
|
||||||
|
_lockmq.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSourceClient::_onPoll(){
|
void AsyncEventSourceClient::_onPoll(){
|
||||||
|
_lockmq.lock();
|
||||||
if(!_messageQueue.isEmpty()){
|
if(!_messageQueue.isEmpty()){
|
||||||
_runQueue();
|
_runQueue();
|
||||||
}
|
}
|
||||||
|
_lockmq.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))){
|
void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))){
|
||||||
_client->close(true);
|
_client->close(true);
|
||||||
}
|
}
|
||||||
@@ -234,17 +245,25 @@ void AsyncEventSourceClient::send(const char *message, const char *event, uint32
|
|||||||
_queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length()));
|
_queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSourceClient::_runQueue(){
|
size_t AsyncEventSourceClient::packetsWaiting() const {
|
||||||
while(!_messageQueue.isEmpty() && _messageQueue.front()->finished()){
|
size_t len;
|
||||||
_messageQueue.remove(_messageQueue.front());
|
_lockmq.lock();
|
||||||
|
len = _messageQueue.length();
|
||||||
|
_lockmq.unlock();
|
||||||
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i)
|
void AsyncEventSourceClient::_runQueue() {
|
||||||
{
|
// Calls to this private method now already protected by _lockmq acquisition
|
||||||
if(!(*i)->sent())
|
// so no extra call of _lockmq.lock() here..
|
||||||
|
for (auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i) {
|
||||||
|
// If it crashes here, iterator (i) has been invalidated as _messageQueue
|
||||||
|
// has been changed... (UL 2020-11-15: Not supposed to happen any more ;-) )
|
||||||
|
if (!(*i)->sent()) {
|
||||||
(*i)->send(_client);
|
(*i)->send(_client);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Handler
|
// Handler
|
||||||
@@ -263,6 +282,10 @@ void AsyncEventSource::onConnect(ArEventHandlerFunction cb){
|
|||||||
_connectcb = cb;
|
_connectcb = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb){
|
||||||
|
_authorizeConnectHandler = cb;
|
||||||
|
}
|
||||||
|
|
||||||
void AsyncEventSource::_addClient(AsyncEventSourceClient * client){
|
void AsyncEventSource::_addClient(AsyncEventSourceClient * client){
|
||||||
/*char * temp = (char *)malloc(2054);
|
/*char * temp = (char *)malloc(2054);
|
||||||
if(temp != NULL){
|
if(temp != NULL){
|
||||||
@@ -276,17 +299,22 @@ void AsyncEventSource::_addClient(AsyncEventSourceClient * client){
|
|||||||
client->write((const char *)temp, 2053);
|
client->write((const char *)temp, 2053);
|
||||||
free(temp);
|
free(temp);
|
||||||
}*/
|
}*/
|
||||||
|
AsyncWebLockGuard l(_client_queue_lock);
|
||||||
_clients.add(client);
|
_clients.add(client);
|
||||||
if(_connectcb)
|
if(_connectcb)
|
||||||
_connectcb(client);
|
_connectcb(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client){
|
void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client){
|
||||||
|
AsyncWebLockGuard l(_client_queue_lock);
|
||||||
_clients.remove(client);
|
_clients.remove(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSource::close(){
|
void AsyncEventSource::close(){
|
||||||
|
// While the whole loop is not done, the linked list is locked and so the
|
||||||
|
// iterator should remain valid even when AsyncEventSource::_handleDisconnect()
|
||||||
|
// is called very early
|
||||||
|
AsyncWebLockGuard l(_client_queue_lock);
|
||||||
for(const auto &c: _clients){
|
for(const auto &c: _clients){
|
||||||
if(c->connected())
|
if(c->connected())
|
||||||
c->close();
|
c->close();
|
||||||
@@ -295,26 +323,25 @@ void AsyncEventSource::close(){
|
|||||||
|
|
||||||
// pmb fix
|
// pmb fix
|
||||||
size_t AsyncEventSource::avgPacketsWaiting() const {
|
size_t AsyncEventSource::avgPacketsWaiting() const {
|
||||||
if(_clients.isEmpty())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
size_t aql = 0;
|
size_t aql = 0;
|
||||||
uint32_t nConnectedClients = 0;
|
uint32_t nConnectedClients = 0;
|
||||||
|
AsyncWebLockGuard l(_client_queue_lock);
|
||||||
|
if (_clients.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
for(const auto &c: _clients){
|
for(const auto &c: _clients){
|
||||||
if(c->connected()) {
|
if(c->connected()) {
|
||||||
aql += c->packetsWaiting();
|
aql += c->packetsWaiting();
|
||||||
++nConnectedClients;
|
++nConnectedClients;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// return aql / nConnectedClients;
|
|
||||||
return ((aql) + (nConnectedClients/2)) / (nConnectedClients); // round up
|
return ((aql) + (nConnectedClients/2)) / (nConnectedClients); // round up
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSource::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){
|
void AsyncEventSource::send(
|
||||||
|
const char *message, const char *event, uint32_t id, uint32_t reconnect){
|
||||||
|
|
||||||
String ev = generateEventMessage(message, event, id, reconnect);
|
String ev = generateEventMessage(message, event, id, reconnect);
|
||||||
|
AsyncWebLockGuard l(_client_queue_lock);
|
||||||
for(const auto &c: _clients){
|
for(const auto &c: _clients){
|
||||||
if(c->connected()) {
|
if(c->connected()) {
|
||||||
c->write(ev.c_str(), ev.length());
|
c->write(ev.c_str(), ev.length());
|
||||||
@@ -323,9 +350,12 @@ void AsyncEventSource::send(const char *message, const char *event, uint32_t id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncEventSource::count() const {
|
size_t AsyncEventSource::count() const {
|
||||||
return _clients.count_if([](AsyncEventSourceClient *c){
|
size_t n_clients;
|
||||||
|
AsyncWebLockGuard l(_client_queue_lock);
|
||||||
|
n_clients = _clients.count_if([](AsyncEventSourceClient *c){
|
||||||
return c->connected();
|
return c->connected();
|
||||||
});
|
});
|
||||||
|
return n_clients;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){
|
bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){
|
||||||
@@ -333,6 +363,7 @@ bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
request->addInterestingHeader(F("Last-Event-ID"));
|
request->addInterestingHeader(F("Last-Event-ID"));
|
||||||
|
request->addInterestingHeader("Cookie");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,6 +371,11 @@ void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){
|
|||||||
if((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) {
|
if((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) {
|
||||||
return request->requestAuthentication();
|
return request->requestAuthentication();
|
||||||
}
|
}
|
||||||
|
if(_authorizeConnectHandler != NULL){
|
||||||
|
if(!_authorizeConnectHandler(request)){
|
||||||
|
return request->send(401);
|
||||||
|
}
|
||||||
|
}
|
||||||
request->send(new AsyncEventSourceResponse(this));
|
request->send(new AsyncEventSourceResponse(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,11 +23,16 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
#include <AsyncTCP.h>
|
#include <AsyncTCP.h>
|
||||||
|
#ifndef SSE_MAX_QUEUED_MESSAGES
|
||||||
#define SSE_MAX_QUEUED_MESSAGES 32
|
#define SSE_MAX_QUEUED_MESSAGES 32
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
#include <ESPAsyncTCP.h>
|
#include <ESPAsyncTCP.h>
|
||||||
|
#ifndef SSE_MAX_QUEUED_MESSAGES
|
||||||
#define SSE_MAX_QUEUED_MESSAGES 8
|
#define SSE_MAX_QUEUED_MESSAGES 8
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
#include "AsyncWebSynchronization.h"
|
#include "AsyncWebSynchronization.h"
|
||||||
@@ -49,6 +54,7 @@ class AsyncEventSource;
|
|||||||
class AsyncEventSourceResponse;
|
class AsyncEventSourceResponse;
|
||||||
class AsyncEventSourceClient;
|
class AsyncEventSourceClient;
|
||||||
typedef std::function<void(AsyncEventSourceClient *client)> ArEventHandlerFunction;
|
typedef std::function<void(AsyncEventSourceClient *client)> ArEventHandlerFunction;
|
||||||
|
typedef std::function<bool(AsyncWebServerRequest *request)> ArAuthorizeConnectHandler;
|
||||||
|
|
||||||
class AsyncEventSourceMessage {
|
class AsyncEventSourceMessage {
|
||||||
private:
|
private:
|
||||||
@@ -72,6 +78,8 @@ class AsyncEventSourceClient {
|
|||||||
AsyncEventSource *_server;
|
AsyncEventSource *_server;
|
||||||
uint32_t _lastId;
|
uint32_t _lastId;
|
||||||
LinkedList<AsyncEventSourceMessage *> _messageQueue;
|
LinkedList<AsyncEventSourceMessage *> _messageQueue;
|
||||||
|
// ArFi 2020-08-27 for protecting/serializing _messageQueue
|
||||||
|
AsyncPlainLock _lockmq;
|
||||||
void _queueMessage(AsyncEventSourceMessage *dataMessage);
|
void _queueMessage(AsyncEventSourceMessage *dataMessage);
|
||||||
void _runQueue();
|
void _runQueue();
|
||||||
|
|
||||||
@@ -86,7 +94,7 @@ class AsyncEventSourceClient {
|
|||||||
void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
|
void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
|
||||||
bool connected() const { return (_client != NULL) && _client->connected(); }
|
bool connected() const { return (_client != NULL) && _client->connected(); }
|
||||||
uint32_t lastId() const { return _lastId; }
|
uint32_t lastId() const { return _lastId; }
|
||||||
size_t packetsWaiting() const { return _messageQueue.length(); }
|
size_t packetsWaiting() const;
|
||||||
|
|
||||||
//system callbacks (do not call)
|
//system callbacks (do not call)
|
||||||
void _onAck(size_t len, uint32_t time);
|
void _onAck(size_t len, uint32_t time);
|
||||||
@@ -99,7 +107,11 @@ class AsyncEventSource: public AsyncWebHandler {
|
|||||||
private:
|
private:
|
||||||
String _url;
|
String _url;
|
||||||
LinkedList<AsyncEventSourceClient *> _clients;
|
LinkedList<AsyncEventSourceClient *> _clients;
|
||||||
|
// Same as for individual messages, protect mutations of _clients list
|
||||||
|
// since simultaneous access from different tasks is possible
|
||||||
|
AsyncWebLock _client_queue_lock;
|
||||||
ArEventHandlerFunction _connectcb;
|
ArEventHandlerFunction _connectcb;
|
||||||
|
ArAuthorizeConnectHandler _authorizeConnectHandler;
|
||||||
public:
|
public:
|
||||||
AsyncEventSource(const String& url);
|
AsyncEventSource(const String& url);
|
||||||
~AsyncEventSource();
|
~AsyncEventSource();
|
||||||
@@ -107,8 +119,10 @@ class AsyncEventSource: public AsyncWebHandler {
|
|||||||
const char * url() const { return _url.c_str(); }
|
const char * url() const { return _url.c_str(); }
|
||||||
void close();
|
void close();
|
||||||
void onConnect(ArEventHandlerFunction cb);
|
void onConnect(ArEventHandlerFunction cb);
|
||||||
|
void authorizeConnect(ArAuthorizeConnectHandler cb);
|
||||||
void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
|
void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
|
||||||
size_t count() const; //number clinets connected
|
// number of clients connected
|
||||||
|
size_t count() const;
|
||||||
size_t avgPacketsWaiting() const;
|
size_t avgPacketsWaiting() const;
|
||||||
|
|
||||||
//system callbacks (do not call)
|
//system callbacks (do not call)
|
||||||
@@ -2,21 +2,53 @@
|
|||||||
/*
|
/*
|
||||||
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
|
||||||
|
|
||||||
|
server.on("/json", HTTP_ANY, [](AsyncWebServerRequest * request) {
|
||||||
|
|
||||||
|
AsyncJsonResponse * response = new AsyncJsonResponse();
|
||||||
|
JsonObject& root = response->getRoot();
|
||||||
|
root["key1"] = "key number one";
|
||||||
|
JsonObject& nested = root.createNestedObject("nested");
|
||||||
|
nested["key1"] = "key number one";
|
||||||
|
|
||||||
|
response->setLength();
|
||||||
|
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>
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
#include <Print.h>
|
#include <Print.h>
|
||||||
|
|
||||||
|
#if ARDUINOJSON_VERSION_MAJOR == 6
|
||||||
|
#ifndef DYNAMIC_JSON_DOCUMENT_SIZE
|
||||||
#define DYNAMIC_JSON_DOCUMENT_SIZE 1024
|
#define DYNAMIC_JSON_DOCUMENT_SIZE 1024
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
constexpr const char * JSON_MIMETYPE = "application/json";
|
constexpr const char * JSON_MIMETYPE = "application/json";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Json Response
|
* Json Response
|
||||||
*/
|
* */
|
||||||
|
|
||||||
class ChunkPrint : public Print {
|
class ChunkPrint : public Print {
|
||||||
private:
|
private:
|
||||||
@@ -97,31 +129,66 @@ class MsgpackAsyncJsonResponse : public AsyncAbstractResponse {
|
|||||||
|
|
||||||
class AsyncJsonResponse : public AsyncAbstractResponse {
|
class AsyncJsonResponse : public AsyncAbstractResponse {
|
||||||
protected:
|
protected:
|
||||||
|
#if ARDUINOJSON_VERSION_MAJOR == 5
|
||||||
|
DynamicJsonBuffer _jsonBuffer;
|
||||||
|
#elif ARDUINOJSON_VERSION_MAJOR == 6
|
||||||
|
DynamicJsonDocument _jsonBuffer;
|
||||||
|
#else
|
||||||
JsonDocument _jsonBuffer;
|
JsonDocument _jsonBuffer;
|
||||||
|
#endif
|
||||||
|
|
||||||
JsonVariant _root;
|
JsonVariant _root;
|
||||||
bool _isValid;
|
bool _isValid;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
#if ARDUINOJSON_VERSION_MAJOR == 5
|
||||||
AsyncJsonResponse(bool isArray = false)
|
AsyncJsonResponse(bool isArray = false)
|
||||||
: _isValid{false} {
|
: _isValid{false} {
|
||||||
_code = 200;
|
_code = 200;
|
||||||
_contentType = JSON_MIMETYPE;
|
_contentType = JSON_MIMETYPE;
|
||||||
if (isArray)
|
if (isArray)
|
||||||
_root = _jsonBuffer.to<JsonArray>();
|
_root = _jsonBuffer.createArray();
|
||||||
|
else
|
||||||
|
_root = _jsonBuffer.createObject();
|
||||||
|
}
|
||||||
|
#elif ARDUINOJSON_VERSION_MAJOR == 6
|
||||||
|
AsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE)
|
||||||
|
: _jsonBuffer(maxJsonBufferSize)
|
||||||
|
, _isValid{false} {
|
||||||
|
_code = 200;
|
||||||
|
_contentType = JSON_MIMETYPE;
|
||||||
|
if (isArray)
|
||||||
|
_root = _jsonBuffer.createNestedArray();
|
||||||
|
else
|
||||||
|
_root = _jsonBuffer.createNestedObject();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
AsyncJsonResponse(bool isArray = false)
|
||||||
|
: _isValid{false} {
|
||||||
|
_code = 200;
|
||||||
|
_contentType = JSON_MIMETYPE;
|
||||||
|
if (isArray)
|
||||||
|
_root = _jsonBuffer.add<JsonArray>();
|
||||||
else
|
else
|
||||||
_root = _jsonBuffer.add<JsonObject>();
|
_root = _jsonBuffer.add<JsonObject>();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
~AsyncJsonResponse() {
|
~AsyncJsonResponse() {
|
||||||
}
|
}
|
||||||
JsonVariant getRoot() {
|
JsonVariant & getRoot() {
|
||||||
return _root;
|
return _root;
|
||||||
}
|
}
|
||||||
bool _sourceValid() const {
|
bool _sourceValid() const {
|
||||||
return _isValid;
|
return _isValid;
|
||||||
}
|
}
|
||||||
size_t setLength() {
|
size_t setLength() {
|
||||||
|
#if ARDUINOJSON_VERSION_MAJOR == 5
|
||||||
|
_contentLength = _root.measureLength();
|
||||||
|
#else
|
||||||
_contentLength = measureJson(_root);
|
_contentLength = measureJson(_root);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (_contentLength) {
|
if (_contentLength) {
|
||||||
_isValid = true;
|
_isValid = true;
|
||||||
}
|
}
|
||||||
@@ -134,18 +201,33 @@ class AsyncJsonResponse : public AsyncAbstractResponse {
|
|||||||
|
|
||||||
size_t _fillBuffer(uint8_t * data, size_t len) {
|
size_t _fillBuffer(uint8_t * data, size_t len) {
|
||||||
ChunkPrint dest(data, _sentLength, len);
|
ChunkPrint dest(data, _sentLength, len);
|
||||||
|
|
||||||
|
#if ARDUINOJSON_VERSION_MAJOR == 5
|
||||||
|
_root.printTo(dest);
|
||||||
|
#else
|
||||||
serializeJson(_root, dest);
|
serializeJson(_root, dest);
|
||||||
|
#endif
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class PrettyAsyncJsonResponse : public AsyncJsonResponse {
|
class PrettyAsyncJsonResponse : public AsyncJsonResponse {
|
||||||
public:
|
public:
|
||||||
|
#if ARDUINOJSON_VERSION_MAJOR == 6
|
||||||
|
PrettyAsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE)
|
||||||
|
: AsyncJsonResponse{isArray, maxJsonBufferSize} {
|
||||||
|
}
|
||||||
|
#else
|
||||||
PrettyAsyncJsonResponse(bool isArray = false)
|
PrettyAsyncJsonResponse(bool isArray = false)
|
||||||
: AsyncJsonResponse{isArray} {
|
: AsyncJsonResponse{isArray} {
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
size_t setLength() {
|
size_t setLength() {
|
||||||
|
#if ARDUINOJSON_VERSION_MAJOR == 5
|
||||||
|
_contentLength = _root.measurePrettyLength();
|
||||||
|
#else
|
||||||
_contentLength = measureJsonPretty(_root);
|
_contentLength = measureJsonPretty(_root);
|
||||||
|
#endif
|
||||||
if (_contentLength) {
|
if (_contentLength) {
|
||||||
_isValid = true;
|
_isValid = true;
|
||||||
}
|
}
|
||||||
@@ -153,12 +235,16 @@ class PrettyAsyncJsonResponse : public AsyncJsonResponse {
|
|||||||
}
|
}
|
||||||
size_t _fillBuffer(uint8_t * data, size_t len) {
|
size_t _fillBuffer(uint8_t * data, size_t len) {
|
||||||
ChunkPrint dest(data, _sentLength, len);
|
ChunkPrint dest(data, _sentLength, len);
|
||||||
|
#if ARDUINOJSON_VERSION_MAJOR == 5
|
||||||
|
_root.prettyPrintTo(dest);
|
||||||
|
#else
|
||||||
serializeJsonPretty(_root, dest);
|
serializeJsonPretty(_root, dest);
|
||||||
|
#endif
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::function<void(AsyncWebServerRequest * request, JsonVariant json)> ArJsonRequestHandlerFunction;
|
typedef std::function<void(AsyncWebServerRequest * request, JsonVariant & json)> ArJsonRequestHandlerFunction;
|
||||||
|
|
||||||
class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
|
class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
|
||||||
private:
|
private:
|
||||||
@@ -167,18 +253,28 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
|
|||||||
WebRequestMethodComposite _method;
|
WebRequestMethodComposite _method;
|
||||||
ArJsonRequestHandlerFunction _onRequest;
|
ArJsonRequestHandlerFunction _onRequest;
|
||||||
size_t _contentLength;
|
size_t _contentLength;
|
||||||
#ifndef ARDUINOJSON_5_COMPATIBILITY
|
#if ARDUINOJSON_VERSION_MAJOR == 6
|
||||||
size_t _maxJsonBufferSize;
|
const size_t maxJsonBufferSize;
|
||||||
#endif
|
#endif
|
||||||
size_t _maxContentLength;
|
size_t _maxContentLength;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
#if ARDUINOJSON_VERSION_MAJOR == 6
|
||||||
|
AsyncCallbackJsonWebHandler(const String & uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE)
|
||||||
|
: _uri(uri)
|
||||||
|
, _method(HTTP_POST | HTTP_PUT | HTTP_PATCH)
|
||||||
|
, _onRequest(onRequest)
|
||||||
|
, maxJsonBufferSize(maxJsonBufferSize)
|
||||||
|
, _maxContentLength(16384) {
|
||||||
|
}
|
||||||
|
#else
|
||||||
AsyncCallbackJsonWebHandler(const String & uri, ArJsonRequestHandlerFunction onRequest)
|
AsyncCallbackJsonWebHandler(const String & uri, ArJsonRequestHandlerFunction onRequest)
|
||||||
: _uri(uri)
|
: _uri(uri)
|
||||||
, _method(HTTP_POST | HTTP_PUT | HTTP_PATCH)
|
, _method(HTTP_POST | HTTP_PUT | HTTP_PATCH)
|
||||||
, _onRequest(onRequest)
|
, _onRequest(onRequest)
|
||||||
, _maxContentLength(16384) {
|
, _maxContentLength(16384) {
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void setMethod(WebRequestMethodComposite method) {
|
void setMethod(WebRequestMethodComposite method) {
|
||||||
_method = method;
|
_method = method;
|
||||||
@@ -186,9 +282,6 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
|
|||||||
void setMaxContentLength(int maxContentLength) {
|
void setMaxContentLength(int maxContentLength) {
|
||||||
_maxContentLength = maxContentLength;
|
_maxContentLength = maxContentLength;
|
||||||
}
|
}
|
||||||
// void setMaxJsonBufferSize(size_t maxJsonBufferSize) {
|
|
||||||
// _maxJsonBufferSize = maxJsonBufferSize;
|
|
||||||
// }
|
|
||||||
void onRequest(ArJsonRequestHandlerFunction fn) {
|
void onRequest(ArJsonRequestHandlerFunction fn) {
|
||||||
_onRequest = fn;
|
_onRequest = fn;
|
||||||
}
|
}
|
||||||
@@ -213,10 +306,22 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
|
|||||||
virtual void handleRequest(AsyncWebServerRequest * request) override final {
|
virtual void handleRequest(AsyncWebServerRequest * request) override final {
|
||||||
if (_onRequest) {
|
if (_onRequest) {
|
||||||
if (request->_tempObject != NULL) {
|
if (request->_tempObject != NULL) {
|
||||||
|
#if ARDUINOJSON_VERSION_MAJOR == 5
|
||||||
|
DynamicJsonBuffer jsonBuffer;
|
||||||
|
JsonVariant json = jsonBuffer.parse((uint8_t *)(request->_tempObject));
|
||||||
|
if (json.success()) {
|
||||||
|
#elif ARDUINOJSON_VERSION_MAJOR == 6
|
||||||
|
DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize);
|
||||||
|
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject));
|
||||||
|
if (!error) {
|
||||||
|
JsonVariant json = jsonBuffer.as<JsonVariant>();
|
||||||
|
#else
|
||||||
JsonDocument jsonBuffer;
|
JsonDocument jsonBuffer;
|
||||||
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject));
|
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject));
|
||||||
if (!error) {
|
if (!error) {
|
||||||
JsonVariant json = jsonBuffer.as<JsonVariant>();
|
JsonVariant json = jsonBuffer.as<JsonVariant>();
|
||||||
|
#endif
|
||||||
|
|
||||||
_onRequest(request, json);
|
_onRequest(request, json);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -243,5 +348,4 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
|
|||||||
return _onRequest ? false : true;
|
return _onRequest ? false : true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
1311
lib/ESPAsyncWebServer/src/AsyncWebSocket.cpp
Normal file
1311
lib/ESPAsyncWebServer/src/AsyncWebSocket.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -27,27 +27,20 @@
|
|||||||
#ifndef WS_MAX_QUEUED_MESSAGES
|
#ifndef WS_MAX_QUEUED_MESSAGES
|
||||||
#define WS_MAX_QUEUED_MESSAGES 32
|
#define WS_MAX_QUEUED_MESSAGES 32
|
||||||
#endif
|
#endif
|
||||||
#ifndef WS_MAX_QUEUED_MESSAGES_SIZE
|
|
||||||
#define WS_MAX_QUEUED_MESSAGES_SIZE 65535
|
|
||||||
#endif
|
|
||||||
#else
|
#else
|
||||||
#include <ESPAsyncTCP.h>
|
#include <ESPAsyncTCP.h>
|
||||||
#ifndef WS_MAX_QUEUED_MESSAGES
|
#ifndef WS_MAX_QUEUED_MESSAGES
|
||||||
#define WS_MAX_QUEUED_MESSAGES 8
|
#define WS_MAX_QUEUED_MESSAGES 8
|
||||||
#endif
|
#endif
|
||||||
#ifndef WS_MAX_QUEUED_MESSAGES_SIZE
|
|
||||||
#define WS_MAX_QUEUED_MESSAGES_SIZE 8192
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
// 0 = disable
|
|
||||||
#ifndef WS_MAX_QUEUED_MESSAGES_MIN_HEAP
|
|
||||||
#define WS_MAX_QUEUED_MESSAGES_MIN_HEAP 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "AsyncWebSynchronization.h"
|
#include "AsyncWebSynchronization.h"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <deque>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
#include <Hash.h>
|
#include <Hash.h>
|
||||||
#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
|
#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
|
||||||
@@ -96,107 +89,41 @@ typedef enum { WS_MSG_SENDING, WS_MSG_SENT, WS_MSG_ERROR } AwsMessageStatus;
|
|||||||
typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType;
|
typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType;
|
||||||
|
|
||||||
class AsyncWebSocketMessageBuffer {
|
class AsyncWebSocketMessageBuffer {
|
||||||
|
friend AsyncWebSocket;
|
||||||
|
friend AsyncWebSocketClient;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t * _data;
|
std::shared_ptr<std::vector<uint8_t>> _buffer;
|
||||||
size_t _len;
|
|
||||||
bool _lock;
|
|
||||||
uint32_t _count;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncWebSocketMessageBuffer();
|
AsyncWebSocketMessageBuffer();
|
||||||
AsyncWebSocketMessageBuffer(size_t size);
|
AsyncWebSocketMessageBuffer(size_t size);
|
||||||
AsyncWebSocketMessageBuffer(uint8_t* data, size_t size);
|
AsyncWebSocketMessageBuffer(uint8_t* data, size_t size);
|
||||||
AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer &);
|
|
||||||
AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBuffer &&);
|
|
||||||
~AsyncWebSocketMessageBuffer();
|
~AsyncWebSocketMessageBuffer();
|
||||||
void operator ++(int i) { (void)i; _count++; }
|
|
||||||
void operator --(int i) { (void)i; if (_count > 0) { _count--; } ; }
|
|
||||||
bool reserve(size_t size);
|
bool reserve(size_t size);
|
||||||
void lock() { _lock = true; }
|
uint8_t* get() { return _buffer->data(); }
|
||||||
void unlock() { _lock = false; }
|
size_t length() const { return _buffer->size(); }
|
||||||
uint8_t * get() { return _data; }
|
|
||||||
size_t length() { return _len; }
|
|
||||||
uint32_t count() { return _count; }
|
|
||||||
bool canDelete() { return (!_count && !_lock); }
|
|
||||||
|
|
||||||
friend AsyncWebSocket;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncWebSocketMessage {
|
class AsyncWebSocketMessage
|
||||||
protected:
|
{
|
||||||
uint8_t _opcode;
|
|
||||||
bool _mask;
|
|
||||||
AwsMessageStatus _status;
|
|
||||||
public:
|
|
||||||
AsyncWebSocketMessage():_opcode(WS_TEXT),_mask(false),_status(WS_MSG_ERROR){}
|
|
||||||
virtual ~AsyncWebSocketMessage(){}
|
|
||||||
virtual void ack(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))){}
|
|
||||||
virtual size_t send(AsyncClient *client __attribute__((unused))){ return 0; }
|
|
||||||
virtual bool finished(){ return _status != WS_MSG_SENDING; }
|
|
||||||
virtual bool betweenFrames() const { return false; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class AsyncWebSocketBasicMessage: public AsyncWebSocketMessage {
|
|
||||||
private:
|
private:
|
||||||
size_t _len;
|
std::shared_ptr<std::vector<uint8_t>> _WSbuffer;
|
||||||
size_t _sent;
|
uint8_t _opcode{WS_TEXT};
|
||||||
size_t _ack;
|
bool _mask{false};
|
||||||
size_t _acked;
|
AwsMessageStatus _status{WS_MSG_ERROR};
|
||||||
uint8_t * _data;
|
size_t _sent{};
|
||||||
public:
|
size_t _ack{};
|
||||||
AsyncWebSocketBasicMessage(const char * data, size_t len, uint8_t opcode=WS_TEXT, bool mask=false);
|
size_t _acked{};
|
||||||
AsyncWebSocketBasicMessage(uint8_t opcode=WS_TEXT, bool mask=false);
|
|
||||||
virtual ~AsyncWebSocketBasicMessage() override;
|
|
||||||
virtual bool betweenFrames() const override { return _acked == _ack; }
|
|
||||||
virtual void ack(size_t len, uint32_t time) override ;
|
|
||||||
virtual size_t send(AsyncClient *client) override ;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AsyncWebSocketMultiMessage: public AsyncWebSocketMessage {
|
|
||||||
private:
|
|
||||||
uint8_t * _data;
|
|
||||||
size_t _len;
|
|
||||||
size_t _sent;
|
|
||||||
size_t _ack;
|
|
||||||
size_t _acked;
|
|
||||||
AsyncWebSocketMessageBuffer * _WSbuffer;
|
|
||||||
public:
|
|
||||||
AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuffer * buffer, uint8_t opcode=WS_TEXT, bool mask=false);
|
|
||||||
virtual ~AsyncWebSocketMultiMessage() override;
|
|
||||||
virtual bool betweenFrames() const override { return _acked == _ack; }
|
|
||||||
virtual void ack(size_t len, uint32_t time) override ;
|
|
||||||
virtual size_t send(AsyncClient *client) override ;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AsyncWebSocketMessageBufferLinkedList : public LinkedList<AsyncWebSocketMessageBuffer *> {
|
|
||||||
public:
|
|
||||||
using T = AsyncWebSocketMessageBuffer *;
|
|
||||||
using LT = LinkedList<T>;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void onRemove(AsyncWebSocketMessageBuffer *t) {
|
|
||||||
_totalSize -= t->length();
|
|
||||||
_totalCount--;
|
|
||||||
delete t;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncWebSocketMessageBufferLinkedList() : LT([this](AsyncWebSocketMessageBuffer *b){ this->onRemove(b); }) {
|
AsyncWebSocketMessage(std::shared_ptr<std::vector<uint8_t>> buffer, uint8_t opcode=WS_TEXT, bool mask=false);
|
||||||
}
|
|
||||||
|
|
||||||
void add(const T& t){
|
bool finished() const { return _status != WS_MSG_SENDING; }
|
||||||
_totalSize += t->length();
|
bool betweenFrames() const { return _acked == _ack; }
|
||||||
_totalCount++;
|
|
||||||
LT::add(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
void ack(size_t len, uint32_t time);
|
||||||
friend class AsyncWebSocket;
|
size_t send(AsyncClient *client);
|
||||||
|
|
||||||
// counters for all web sockets
|
|
||||||
static size_t _totalCount;
|
|
||||||
static size_t _totalSize;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncWebSocketClient {
|
class AsyncWebSocketClient {
|
||||||
@@ -206,8 +133,10 @@ class AsyncWebSocketClient {
|
|||||||
uint32_t _clientId;
|
uint32_t _clientId;
|
||||||
AwsClientStatus _status;
|
AwsClientStatus _status;
|
||||||
|
|
||||||
LinkedList<AsyncWebSocketControl *> _controlQueue;
|
AsyncWebLock _lock;
|
||||||
LinkedList<AsyncWebSocketMessage *> _messageQueue;
|
|
||||||
|
std::deque<AsyncWebSocketControl> _controlQueue;
|
||||||
|
std::deque<AsyncWebSocketMessage> _messageQueue;
|
||||||
|
|
||||||
uint8_t _pstate;
|
uint8_t _pstate;
|
||||||
AwsFrameInfo _pinfo;
|
AwsFrameInfo _pinfo;
|
||||||
@@ -215,9 +144,10 @@ class AsyncWebSocketClient {
|
|||||||
uint32_t _lastMessageTime;
|
uint32_t _lastMessageTime;
|
||||||
uint32_t _keepAlivePeriod;
|
uint32_t _keepAlivePeriod;
|
||||||
|
|
||||||
void _queueMessage(AsyncWebSocketMessage *dataMessage);
|
void _queueControl(uint8_t opcode, const uint8_t *data=NULL, size_t len=0, bool mask=false);
|
||||||
void _queueControl(AsyncWebSocketControl *controlMessage);
|
void _queueMessage(std::shared_ptr<std::vector<uint8_t>> buffer, uint8_t opcode=WS_TEXT, bool mask=false);
|
||||||
void _runQueue();
|
void _runQueue();
|
||||||
|
void _clearQueue();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void *_tempObject;
|
void *_tempObject;
|
||||||
@@ -226,18 +156,22 @@ class AsyncWebSocketClient {
|
|||||||
~AsyncWebSocketClient();
|
~AsyncWebSocketClient();
|
||||||
|
|
||||||
//client id increments for the given server
|
//client id increments for the given server
|
||||||
uint32_t id(){ return _clientId; }
|
uint32_t id() const { return _clientId; }
|
||||||
AwsClientStatus status(){ return _status; }
|
AwsClientStatus status() const { return _status; }
|
||||||
AsyncClient* client() { return _client; }
|
AsyncClient* client() { return _client; }
|
||||||
|
const AsyncClient* client() const { return _client; }
|
||||||
AsyncWebSocket *server(){ return _server; }
|
AsyncWebSocket *server(){ return _server; }
|
||||||
|
const AsyncWebSocket *server() const { return _server; }
|
||||||
AwsFrameInfo const &pinfo() const { return _pinfo; }
|
AwsFrameInfo const &pinfo() const { return _pinfo; }
|
||||||
|
|
||||||
IPAddress remoteIP();
|
IPAddress remoteIP() const;
|
||||||
uint16_t remotePort();
|
uint16_t remotePort() const;
|
||||||
|
|
||||||
|
bool shouldBeDeleted() const { return !_client; }
|
||||||
|
|
||||||
//control frames
|
//control frames
|
||||||
void close(uint16_t code=0, const char * message=NULL);
|
void close(uint16_t code=0, const char * message=NULL);
|
||||||
void ping(uint8_t *data=NULL, size_t len=0);
|
void ping(const uint8_t *data=NULL, size_t len=0);
|
||||||
|
|
||||||
//set auto-ping period in seconds. disabled if zero (default)
|
//set auto-ping period in seconds. disabled if zero (default)
|
||||||
void keepAlivePeriod(uint16_t seconds){
|
void keepAlivePeriod(uint16_t seconds){
|
||||||
@@ -248,32 +182,32 @@ class AsyncWebSocketClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//data packets
|
//data packets
|
||||||
void message(AsyncWebSocketMessage *message){ _queueMessage(message); }
|
void message(std::shared_ptr<std::vector<uint8_t>> buffer, uint8_t opcode=WS_TEXT, bool mask=false) { _queueMessage(buffer, opcode, mask); }
|
||||||
bool queueIsFull();
|
bool queueIsFull() const;
|
||||||
|
size_t queueLen() const;
|
||||||
|
|
||||||
size_t printf(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
|
size_t printf(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
|
||||||
#ifndef ESP32
|
#ifndef ESP32
|
||||||
size_t printf_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3)));
|
size_t printf_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3)));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void text(std::shared_ptr<std::vector<uint8_t>> buffer);
|
||||||
|
void text(const uint8_t *message, size_t len);
|
||||||
void text(const char *message, size_t len);
|
void text(const char *message, size_t len);
|
||||||
void text(const char *message);
|
void text(const char *message);
|
||||||
void text(uint8_t * message, size_t len);
|
|
||||||
void text(char * message);
|
|
||||||
void text(const String &message);
|
void text(const String &message);
|
||||||
void text(const __FlashStringHelper *data);
|
void text(const __FlashStringHelper *message);
|
||||||
void text(AsyncWebSocketMessageBuffer *buffer);
|
void text(AsyncWebSocketMessageBuffer *buffer);
|
||||||
|
|
||||||
|
void binary(std::shared_ptr<std::vector<uint8_t>> buffer);
|
||||||
|
void binary(const uint8_t *message, size_t len);
|
||||||
void binary(const char * message, size_t len);
|
void binary(const char * message, size_t len);
|
||||||
void binary(const char * message);
|
void binary(const char * message);
|
||||||
void binary(uint8_t * message, size_t len);
|
|
||||||
void binary(char * message);
|
|
||||||
void binary(const String &message);
|
void binary(const String &message);
|
||||||
void binary(const __FlashStringHelper *data, size_t len);
|
void binary(const __FlashStringHelper *message, size_t len);
|
||||||
void binary(AsyncWebSocketMessageBuffer *buffer);
|
void binary(AsyncWebSocketMessageBuffer *buffer);
|
||||||
|
|
||||||
bool canSend() { return !_queueIsFull(); }
|
bool canSend() const;
|
||||||
|
|
||||||
bool _queueIsFull() const;
|
|
||||||
|
|
||||||
//system callbacks (do not call)
|
//system callbacks (do not call)
|
||||||
void _onAck(size_t len, uint32_t time);
|
void _onAck(size_t len, uint32_t time);
|
||||||
@@ -284,17 +218,17 @@ class AsyncWebSocketClient {
|
|||||||
void _onData(void *pbuf, size_t plen);
|
void _onData(void *pbuf, size_t plen);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef std::function<bool(AsyncWebServerRequest *request)> AwsHandshakeHandler;
|
||||||
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;
|
||||||
|
|
||||||
//WebServer Handler implementation that plays the role of a socket server
|
//WebServer Handler implementation that plays the role of a socket server
|
||||||
class AsyncWebSocket: public AsyncWebHandler {
|
class AsyncWebSocket: public AsyncWebHandler {
|
||||||
public:
|
|
||||||
typedef LinkedList<AsyncWebSocketClient *> AsyncWebSocketClientLinkedList;
|
|
||||||
private:
|
private:
|
||||||
String _url;
|
String _url;
|
||||||
AsyncWebSocketClientLinkedList _clients;
|
std::list<AsyncWebSocketClient> _clients;
|
||||||
uint32_t _cNextId;
|
uint32_t _cNextId;
|
||||||
AwsEventHandler _eventHandler;
|
AwsEventHandler _eventHandler;
|
||||||
|
AwsHandshakeHandler _handshakeHandler;
|
||||||
bool _enabled;
|
bool _enabled;
|
||||||
AsyncWebLock _lock;
|
AsyncWebLock _lock;
|
||||||
|
|
||||||
@@ -315,42 +249,37 @@ class AsyncWebSocket: public AsyncWebHandler {
|
|||||||
void closeAll(uint16_t code=0, const char * message=NULL);
|
void closeAll(uint16_t code=0, const char * message=NULL);
|
||||||
void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS);
|
void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS);
|
||||||
|
|
||||||
void ping(uint32_t id, uint8_t *data=NULL, size_t len=0);
|
void ping(uint32_t id, const uint8_t *data=NULL, size_t len=0);
|
||||||
void pingAll(uint8_t *data=NULL, size_t len=0); // done
|
void pingAll(const uint8_t *data=NULL, size_t len=0); // done
|
||||||
|
|
||||||
|
void text(uint32_t id, const uint8_t * message, size_t len);
|
||||||
void text(uint32_t id, const char *message, size_t len);
|
void text(uint32_t id, const char *message, size_t len);
|
||||||
void text(uint32_t id, const char *message);
|
void text(uint32_t id, const char *message);
|
||||||
void text(uint32_t id, uint8_t * message, size_t len);
|
|
||||||
void text(uint32_t id, char * message);
|
|
||||||
void text(uint32_t id, const String &message);
|
void text(uint32_t id, const String &message);
|
||||||
void text(uint32_t id, const __FlashStringHelper *message);
|
void text(uint32_t id, const __FlashStringHelper *message);
|
||||||
|
|
||||||
|
void textAll(std::shared_ptr<std::vector<uint8_t>> buffer);
|
||||||
|
void textAll(const uint8_t *message, size_t len);
|
||||||
void textAll(const char * message, size_t len);
|
void textAll(const char * message, size_t len);
|
||||||
void textAll(const char * message);
|
void textAll(const char * message);
|
||||||
void textAll(uint8_t * message, size_t len);
|
|
||||||
void textAll(char * message);
|
|
||||||
void textAll(const String &message);
|
void textAll(const String &message);
|
||||||
void textAll(const __FlashStringHelper *message); // need to convert
|
void textAll(const __FlashStringHelper *message); // need to convert
|
||||||
void textAll(AsyncWebSocketMessageBuffer *buffer);
|
void textAll(AsyncWebSocketMessageBuffer *buffer);
|
||||||
|
|
||||||
|
void binary(uint32_t id, const uint8_t *message, size_t len);
|
||||||
void binary(uint32_t id, const char *message, size_t len);
|
void binary(uint32_t id, const char *message, size_t len);
|
||||||
void binary(uint32_t id, const char *message);
|
void binary(uint32_t id, const char *message);
|
||||||
void binary(uint32_t id, uint8_t * message, size_t len);
|
|
||||||
void binary(uint32_t id, char * message);
|
|
||||||
void binary(uint32_t id, const String &message);
|
void binary(uint32_t id, const String &message);
|
||||||
void binary(uint32_t id, const __FlashStringHelper *message, size_t len);
|
void binary(uint32_t id, const __FlashStringHelper *message, size_t len);
|
||||||
|
|
||||||
|
void binaryAll(std::shared_ptr<std::vector<uint8_t>> buffer);
|
||||||
|
void binaryAll(const uint8_t *message, size_t len);
|
||||||
void binaryAll(const char *message, size_t len);
|
void binaryAll(const char *message, size_t len);
|
||||||
void binaryAll(const char *message);
|
void binaryAll(const char *message);
|
||||||
void binaryAll(uint8_t * message, size_t len);
|
|
||||||
void binaryAll(char * message);
|
|
||||||
void binaryAll(const String &message);
|
void binaryAll(const String &message);
|
||||||
void binaryAll(const __FlashStringHelper *message, size_t len);
|
void binaryAll(const __FlashStringHelper *message, size_t len);
|
||||||
void binaryAll(AsyncWebSocketMessageBuffer *buffer);
|
void binaryAll(AsyncWebSocketMessageBuffer *buffer);
|
||||||
|
|
||||||
void message(uint32_t id, AsyncWebSocketMessage *message);
|
|
||||||
void messageAll(AsyncWebSocketMultiMessage *message);
|
|
||||||
|
|
||||||
size_t printf(uint32_t id, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
|
size_t printf(uint32_t id, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
|
||||||
size_t printfAll(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
|
size_t printfAll(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
|
||||||
#ifndef ESP32
|
#ifndef ESP32
|
||||||
@@ -363,10 +292,14 @@ class AsyncWebSocket: public AsyncWebHandler {
|
|||||||
_eventHandler = handler;
|
_eventHandler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handshake Handler
|
||||||
|
void handleHandshake(AwsHandshakeHandler handler){
|
||||||
|
_handshakeHandler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
//system callbacks (do not call)
|
//system callbacks (do not call)
|
||||||
uint32_t _getNextId(){ return _cNextId++; }
|
uint32_t _getNextId(){ return _cNextId++; }
|
||||||
void _addClient(AsyncWebSocketClient * client);
|
AsyncWebSocketClient *_newClient(AsyncWebServerRequest *request);
|
||||||
void _handleDisconnect(AsyncWebSocketClient * client);
|
|
||||||
void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len);
|
void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len);
|
||||||
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;
|
||||||
@@ -375,61 +308,8 @@ class AsyncWebSocket: public AsyncWebHandler {
|
|||||||
// messagebuffer functions/objects.
|
// messagebuffer functions/objects.
|
||||||
AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0);
|
AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0);
|
||||||
AsyncWebSocketMessageBuffer * makeBuffer(uint8_t * data, size_t size);
|
AsyncWebSocketMessageBuffer * makeBuffer(uint8_t * data, size_t size);
|
||||||
AsyncWebSocketMessageBufferLinkedList _buffers;
|
|
||||||
void _cleanBuffers();
|
|
||||||
|
|
||||||
AsyncWebSocketClientLinkedList getClients() const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
#ifndef DEBUG_AWS_QUEUE_COUNTERS
|
|
||||||
#define DEBUG_AWS_QUEUE_COUNTERS 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if DEBUG_AWS_QUEUE_COUNTERS
|
|
||||||
// total for all sockets
|
|
||||||
size_t getQueuedMessageCount() const {
|
|
||||||
_verifyCounters();
|
|
||||||
return AsyncWebSocketMessageBufferLinkedList::_totalCount;
|
|
||||||
}
|
|
||||||
size_t getQueuedMessageSize() const {
|
|
||||||
_verifyCounters();
|
|
||||||
return AsyncWebSocketMessageBufferLinkedList::_totalSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t _getQueuedMessageCount() {
|
|
||||||
return AsyncWebSocketMessageBufferLinkedList::_totalCount;
|
|
||||||
}
|
|
||||||
static size_t _getQueuedMessageSize() {
|
|
||||||
return AsyncWebSocketMessageBufferLinkedList::_totalSize;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
void _verifyCounters() const {
|
|
||||||
size_t t=0,c=0;
|
|
||||||
for(const auto b: _buffers) {
|
|
||||||
t+=b->length();
|
|
||||||
c++;
|
|
||||||
}
|
|
||||||
if (AsyncWebSocketMessageBufferLinkedList::_totalSize!=t || AsyncWebSocketMessageBufferLinkedList::_totalCount!=c) {
|
|
||||||
::printf(PSTR("AsyncWebSocketMessageBufferLinkedList size %u=%u cnt %u=%u\n"), AsyncWebSocketMessageBufferLinkedList::_totalSize, t,AsyncWebSocketMessageBufferLinkedList::_totalCount, c);
|
|
||||||
panic();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
size_t getQueuedMessageCount() const {
|
|
||||||
return _getQueuedMessageCount();
|
|
||||||
}
|
|
||||||
size_t getQueuedMessageSize() const {
|
|
||||||
return _getQueuedMessageSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t _getQueuedMessageCount() {
|
|
||||||
return AsyncWebSocketMessageBufferLinkedList::_totalCount;
|
|
||||||
}
|
|
||||||
static size_t _getQueuedMessageSize() {
|
|
||||||
return AsyncWebSocketMessageBufferLinkedList::_totalSize;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
const std::list<AsyncWebSocketClient> &getClients() const { return _clients; }
|
||||||
};
|
};
|
||||||
|
|
||||||
//WebServer response to authenticate the socket and detach the tcp client from the web server request
|
//WebServer response to authenticate the socket and detach the tcp client from the web server request
|
||||||
134
lib/ESPAsyncWebServer/src/AsyncWebSynchronization.h
Normal file
134
lib/ESPAsyncWebServer/src/AsyncWebSynchronization.h
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
#ifndef ASYNCWEBSYNCHRONIZATION_H_
|
||||||
|
#define ASYNCWEBSYNCHRONIZATION_H_
|
||||||
|
|
||||||
|
// Synchronisation is only available on ESP32, as the ESP8266 isn't using FreeRTOS by default
|
||||||
|
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
|
|
||||||
|
// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore
|
||||||
|
// Modified 'AsyncWebLock' to just only use mutex since pxCurrentTCB is not
|
||||||
|
// always available. According to example by Arjan Filius, changed name,
|
||||||
|
// added unimplemented version for ESP8266
|
||||||
|
class AsyncPlainLock
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SemaphoreHandle_t _lock;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AsyncPlainLock() {
|
||||||
|
_lock = xSemaphoreCreateBinary();
|
||||||
|
// In this fails, the system is likely that much out of memory that
|
||||||
|
// we should abort anyways. If assertions are disabled, nothing is lost..
|
||||||
|
assert(_lock);
|
||||||
|
xSemaphoreGive(_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
~AsyncPlainLock() {
|
||||||
|
vSemaphoreDelete(_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lock() const {
|
||||||
|
xSemaphoreTake(_lock, portMAX_DELAY);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock() const {
|
||||||
|
xSemaphoreGive(_lock);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore
|
||||||
|
class AsyncWebLock
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SemaphoreHandle_t _lock;
|
||||||
|
mutable TaskHandle_t _lockedBy{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
AsyncWebLock()
|
||||||
|
{
|
||||||
|
_lock = xSemaphoreCreateBinary();
|
||||||
|
// In this fails, the system is likely that much out of memory that
|
||||||
|
// we should abort anyways. If assertions are disabled, nothing is lost..
|
||||||
|
assert(_lock);
|
||||||
|
_lockedBy = NULL;
|
||||||
|
xSemaphoreGive(_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
~AsyncWebLock() {
|
||||||
|
vSemaphoreDelete(_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lock() const {
|
||||||
|
const auto currentTask = xTaskGetCurrentTaskHandle();
|
||||||
|
if (_lockedBy != currentTask) {
|
||||||
|
xSemaphoreTake(_lock, portMAX_DELAY);
|
||||||
|
_lockedBy = currentTask;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock() const {
|
||||||
|
_lockedBy = NULL;
|
||||||
|
xSemaphoreGive(_lock);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// This is the 8266 version of the Sync Lock which is currently unimplemented
|
||||||
|
class AsyncWebLock
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
AsyncWebLock() {
|
||||||
|
}
|
||||||
|
|
||||||
|
~AsyncWebLock() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lock() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock() const {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Same for AsyncPlainLock, for ESP8266 this is just the unimplemented version above.
|
||||||
|
using AsyncPlainLock = AsyncWebLock;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class AsyncWebLockGuard
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
const AsyncWebLock *_lock;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AsyncWebLockGuard(const AsyncWebLock &l) {
|
||||||
|
if (l.lock()) {
|
||||||
|
_lock = &l;
|
||||||
|
} else {
|
||||||
|
_lock = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~AsyncWebLockGuard() {
|
||||||
|
if (_lock) {
|
||||||
|
_lock->unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock() {
|
||||||
|
if (_lock) {
|
||||||
|
_lock->unlock();
|
||||||
|
_lock = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ASYNCWEBSYNCHRONIZATION_H_
|
||||||
@@ -24,6 +24,8 @@
|
|||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <list>
|
||||||
|
#include <vector>
|
||||||
#include "FS.h"
|
#include "FS.h"
|
||||||
|
|
||||||
#include "StringArray.h"
|
#include "StringArray.h"
|
||||||
@@ -38,14 +40,18 @@
|
|||||||
#error Platform not supported
|
#error Platform not supported
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define ASYNCWEBSERVER_VERSION "2.6.1"
|
||||||
|
#define ASYNCWEBSERVER_VERSION_MAJOR 2
|
||||||
|
#define ASYNCWEBSERVER_VERSION_MINOR 6
|
||||||
|
#define ASYNCWEBSERVER_VERSION_REVISION 1
|
||||||
|
#define ASYNCWEBSERVER_FORK_mathieucarbou
|
||||||
|
|
||||||
#ifdef ASYNCWEBSERVER_REGEX
|
#ifdef ASYNCWEBSERVER_REGEX
|
||||||
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE
|
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE
|
||||||
#else
|
#else
|
||||||
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined")))
|
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined")))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define DEBUGF(...) //Serial.printf(__VA_ARGS__)
|
|
||||||
|
|
||||||
class AsyncWebServer;
|
class AsyncWebServer;
|
||||||
class AsyncWebServerRequest;
|
class AsyncWebServerRequest;
|
||||||
class AsyncWebServerResponse;
|
class AsyncWebServerResponse;
|
||||||
@@ -121,6 +127,9 @@ class AsyncWebHeader {
|
|||||||
String _value;
|
String _value;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
AsyncWebHeader() = default;
|
||||||
|
AsyncWebHeader(const AsyncWebHeader &) = default;
|
||||||
|
|
||||||
AsyncWebHeader(const String& name, const String& value): _name(name), _value(value){}
|
AsyncWebHeader(const String& name, const String& value): _name(name), _value(value){}
|
||||||
AsyncWebHeader(const String& data): _name(), _value(){
|
AsyncWebHeader(const String& data): _name(), _value(){
|
||||||
if(!data) return;
|
if(!data) return;
|
||||||
@@ -129,10 +138,12 @@ class AsyncWebHeader {
|
|||||||
_name = data.substring(0, index);
|
_name = data.substring(0, index);
|
||||||
_value = data.substring(index + 2);
|
_value = data.substring(index + 2);
|
||||||
}
|
}
|
||||||
~AsyncWebHeader(){}
|
|
||||||
|
AsyncWebHeader &operator=(const AsyncWebHeader &) = default;
|
||||||
|
|
||||||
const String& name() const { return _name; }
|
const String& name() const { return _name; }
|
||||||
const String& value() const { return _value; }
|
const String& value() const { return _value; }
|
||||||
String toString() const { return String(_name + F(": ") + _value + F("\r\n")); }
|
String toString() const { return _name + F(": ") + _value + F("\r\n"); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -149,13 +160,12 @@ class AsyncWebServerRequest {
|
|||||||
using FS = fs::FS;
|
using FS = fs::FS;
|
||||||
friend class AsyncWebServer;
|
friend class AsyncWebServer;
|
||||||
friend class AsyncCallbackWebHandler;
|
friend class AsyncCallbackWebHandler;
|
||||||
friend class HttpCookieHeader;
|
|
||||||
private:
|
private:
|
||||||
AsyncClient* _client;
|
AsyncClient* _client;
|
||||||
AsyncWebServer* _server;
|
AsyncWebServer* _server;
|
||||||
AsyncWebHandler* _handler;
|
AsyncWebHandler* _handler;
|
||||||
AsyncWebServerResponse* _response;
|
AsyncWebServerResponse* _response;
|
||||||
StringArray _interestingHeaders;
|
std::vector<String> _interestingHeaders;
|
||||||
ArDisconnectHandler _onDisconnectfn;
|
ArDisconnectHandler _onDisconnectfn;
|
||||||
|
|
||||||
String _temp;
|
String _temp;
|
||||||
@@ -177,9 +187,9 @@ class AsyncWebServerRequest {
|
|||||||
size_t _contentLength;
|
size_t _contentLength;
|
||||||
size_t _parsedLength;
|
size_t _parsedLength;
|
||||||
|
|
||||||
LinkedList<AsyncWebHeader *> _headers;
|
std::list<AsyncWebHeader> _headers;
|
||||||
LinkedList<AsyncWebParameter *> _params;
|
LinkedList<AsyncWebParameter *> _params;
|
||||||
LinkedList<String *> _pathParams;
|
std::vector<String> _pathParams;
|
||||||
|
|
||||||
uint8_t _multiParseState;
|
uint8_t _multiParseState;
|
||||||
uint8_t _boundaryPosition;
|
uint8_t _boundaryPosition;
|
||||||
@@ -229,8 +239,8 @@ class AsyncWebServerRequest {
|
|||||||
const String& contentType() const { return _contentType; }
|
const String& contentType() const { return _contentType; }
|
||||||
size_t contentLength() const { return _contentLength; }
|
size_t contentLength() const { return _contentLength; }
|
||||||
bool multipart() const { return _isMultipart; }
|
bool multipart() const { return _isMultipart; }
|
||||||
const char *methodToString() const;
|
const __FlashStringHelper *methodToString() const;
|
||||||
const char *requestedConnTypeToString() const;
|
const __FlashStringHelper *requestedConnTypeToString() const;
|
||||||
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);
|
||||||
@@ -271,9 +281,12 @@ class AsyncWebServerRequest {
|
|||||||
bool hasHeader(const String& name) const; // check if header exists
|
bool hasHeader(const String& name) const; // check if header exists
|
||||||
bool hasHeader(const __FlashStringHelper * data) const; // check if header exists
|
bool hasHeader(const __FlashStringHelper * data) const; // check if header exists
|
||||||
|
|
||||||
AsyncWebHeader* getHeader(const String& name) const;
|
AsyncWebHeader* getHeader(const String& name);
|
||||||
AsyncWebHeader* getHeader(const __FlashStringHelper * data) const;
|
const AsyncWebHeader* getHeader(const String& name) const;
|
||||||
AsyncWebHeader* getHeader(size_t num) const;
|
AsyncWebHeader* getHeader(const __FlashStringHelper * data);
|
||||||
|
const AsyncWebHeader* getHeader(const __FlashStringHelper * data) const;
|
||||||
|
AsyncWebHeader* getHeader(size_t num);
|
||||||
|
const AsyncWebHeader* getHeader(size_t num) const;
|
||||||
|
|
||||||
size_t params() const; // get arguments count
|
size_t params() const; // get arguments count
|
||||||
bool hasParam(const String& name, bool post=false, bool file=false) const;
|
bool hasParam(const String& name, bool post=false, bool file=false) const;
|
||||||
@@ -372,7 +385,7 @@ typedef enum {
|
|||||||
class AsyncWebServerResponse {
|
class AsyncWebServerResponse {
|
||||||
protected:
|
protected:
|
||||||
int _code;
|
int _code;
|
||||||
LinkedList<AsyncWebHeader *> _headers;
|
std::list<AsyncWebHeader> _headers;
|
||||||
String _contentType;
|
String _contentType;
|
||||||
size_t _contentLength;
|
size_t _contentLength;
|
||||||
bool _sendContentLength;
|
bool _sendContentLength;
|
||||||
@@ -455,17 +468,16 @@ class AsyncWebServer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class DefaultHeaders {
|
class DefaultHeaders {
|
||||||
using headers_t = LinkedList<AsyncWebHeader *>;
|
using headers_t = std::list<AsyncWebHeader>;
|
||||||
headers_t _headers;
|
headers_t _headers;
|
||||||
|
|
||||||
DefaultHeaders()
|
|
||||||
:_headers(headers_t([](AsyncWebHeader *h){ delete h; }))
|
|
||||||
{}
|
|
||||||
public:
|
public:
|
||||||
using ConstIterator = headers_t::ConstIterator;
|
DefaultHeaders() = default;
|
||||||
|
|
||||||
|
using ConstIterator = headers_t::const_iterator;
|
||||||
|
|
||||||
void addHeader(const String& name, const String& value){
|
void addHeader(const String& name, const String& value){
|
||||||
_headers.add(new AsyncWebHeader(name, value));
|
_headers.emplace_back(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstIterator begin() const { return _headers.begin(); }
|
ConstIterator begin() const { return _headers.begin(); }
|
||||||
@@ -473,6 +485,7 @@ public:
|
|||||||
|
|
||||||
DefaultHeaders(DefaultHeaders const &) = delete;
|
DefaultHeaders(DefaultHeaders const &) = delete;
|
||||||
DefaultHeaders &operator=(DefaultHeaders const &) = delete;
|
DefaultHeaders &operator=(DefaultHeaders const &) = delete;
|
||||||
|
|
||||||
static DefaultHeaders &Instance() {
|
static DefaultHeaders &Instance() {
|
||||||
static DefaultHeaders instance;
|
static DefaultHeaders instance;
|
||||||
return instance;
|
return instance;
|
||||||
2
lib/ESPAsyncWebServer/src/ESP_Async_WebServer.h
Normal file
2
lib/ESPAsyncWebServer/src/ESP_Async_WebServer.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// to please Arduino Lint
|
||||||
|
#include "ESPAsyncWebServer.h"
|
||||||
@@ -171,23 +171,4 @@ class LinkedList {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class StringArray : public LinkedList<String> {
|
|
||||||
public:
|
|
||||||
|
|
||||||
StringArray() : LinkedList(nullptr) {}
|
|
||||||
|
|
||||||
bool containsIgnoreCase(const String& str){
|
|
||||||
for (const auto& s : *this) {
|
|
||||||
if (str.equalsIgnoreCase(s)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* STRINGARRAY_H_ */
|
#endif /* STRINGARRAY_H_ */
|
||||||
@@ -36,6 +36,12 @@ bool checkBasicAuthentication(const char * hash, const char * username, const ch
|
|||||||
size_t toencodeLen = strlen(username)+strlen(password)+1;
|
size_t toencodeLen = strlen(username)+strlen(password)+1;
|
||||||
size_t encodedLen = base64_encode_expected_len(toencodeLen);
|
size_t encodedLen = base64_encode_expected_len(toencodeLen);
|
||||||
if(strlen(hash) != encodedLen)
|
if(strlen(hash) != encodedLen)
|
||||||
|
// Fix from https://github.com/me-no-dev/ESPAsyncWebServer/issues/667
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
if(strlen(hash) != encodedLen)
|
||||||
|
#else
|
||||||
|
if (strlen(hash) != encodedLen - 1)
|
||||||
|
#endif
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
char *toencode = new char[toencodeLen+1];
|
char *toencode = new char[toencodeLen+1];
|
||||||
@@ -71,9 +77,9 @@ static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or mo
|
|||||||
memset(_buf, 0x00, 16);
|
memset(_buf, 0x00, 16);
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
mbedtls_md5_init(&_ctx);
|
mbedtls_md5_init(&_ctx);
|
||||||
mbedtls_md5_update (&_ctx,data,len);
|
mbedtls_md5_starts_ret(&_ctx);
|
||||||
mbedtls_md5_finish(&_ctx,data);
|
mbedtls_md5_update_ret(&_ctx, data, len);
|
||||||
mbedtls_internal_md5_process( &_ctx ,data);
|
mbedtls_md5_finish_ret(&_ctx, _buf);
|
||||||
#else
|
#else
|
||||||
MD5Init(&_ctx);
|
MD5Init(&_ctx);
|
||||||
MD5Update(&_ctx, data, len);
|
MD5Update(&_ctx, data, len);
|
||||||
@@ -141,7 +147,7 @@ String requestDigestAuthentication(const char * realm){
|
|||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checkDigestAuthentication(const char * header, const char *method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri){
|
bool checkDigestAuthentication(const char * header, const __FlashStringHelper *method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri){
|
||||||
if(username == NULL || password == NULL || header == NULL || method == NULL){
|
if(username == NULL || password == NULL || header == NULL || method == NULL){
|
||||||
//os_printf("AUTH FAIL: missing requred fields\n");
|
//os_printf("AUTH FAIL: missing requred fields\n");
|
||||||
return false;
|
return false;
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
bool checkBasicAuthentication(const char * header, const char * username, const char * password);
|
bool checkBasicAuthentication(const char * header, const char * username, const char * password);
|
||||||
String requestDigestAuthentication(const char * realm);
|
String requestDigestAuthentication(const char * realm);
|
||||||
bool checkDigestAuthentication(const char * header, const char *method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri);
|
bool checkDigestAuthentication(const char * header, const __FlashStringHelper *method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri);
|
||||||
|
|
||||||
//for storing hashed versions on the device that can be authenticated against
|
//for storing hashed versions on the device that can be authenticated against
|
||||||
String generateDigestHash(const char * username, const char * password, const char * realm);
|
String generateDigestHash(const char * username, const char * password, const char * realm);
|
||||||
@@ -105,6 +105,13 @@ class AsyncCallbackWebHandler: public AsyncWebHandler {
|
|||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
|
if (_uri.length() && _uri.startsWith("/*.")) {
|
||||||
|
String uriTemplate = String (_uri);
|
||||||
|
uriTemplate = uriTemplate.substring(uriTemplate.lastIndexOf("."));
|
||||||
|
if (!request->url().endsWith(uriTemplate))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
if (_uri.length() && _uri.endsWith("*")) {
|
if (_uri.length() && _uri.endsWith("*")) {
|
||||||
String uriTemplate = String(_uri);
|
String uriTemplate = String(_uri);
|
||||||
uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
|
uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
|
||||||
@@ -119,16 +126,22 @@ class AsyncCallbackWebHandler: public AsyncWebHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void handleRequest(AsyncWebServerRequest *request) override final {
|
virtual void handleRequest(AsyncWebServerRequest *request) override final {
|
||||||
|
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
||||||
|
return request->requestAuthentication();
|
||||||
if(_onRequest)
|
if(_onRequest)
|
||||||
_onRequest(request);
|
_onRequest(request);
|
||||||
else
|
else
|
||||||
request->send(500);
|
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 handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final {
|
||||||
|
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
||||||
|
return request->requestAuthentication();
|
||||||
if(_onUpload)
|
if(_onUpload)
|
||||||
_onUpload(request, filename, index, data, len, final);
|
_onUpload(request, filename, index, data, len, final);
|
||||||
}
|
}
|
||||||
virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
|
virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
|
||||||
|
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
||||||
|
return request->requestAuthentication();
|
||||||
if(_onBody)
|
if(_onBody)
|
||||||
_onBody(request, data, len, index, total);
|
_onBody(request, data, len, index, total);
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,7 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_
|
|||||||
}
|
}
|
||||||
|
|
||||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_modified){
|
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_modified){
|
||||||
_last_modified = String(last_modified);
|
_last_modified = last_modified;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +99,6 @@ bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){
|
|||||||
if(_cache_control.length())
|
if(_cache_control.length())
|
||||||
request->addInterestingHeader(F("If-None-Match"));
|
request->addInterestingHeader(F("If-None-Match"));
|
||||||
|
|
||||||
DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,20 +145,28 @@ bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const St
|
|||||||
String gzip = path + F(".gz");
|
String gzip = path + F(".gz");
|
||||||
|
|
||||||
if (_gzipFirst) {
|
if (_gzipFirst) {
|
||||||
|
if (_fs.exists(gzip)) {
|
||||||
request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read);
|
request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read);
|
||||||
gzipFound = FILE_IS_REAL(request->_tempFile);
|
gzipFound = FILE_IS_REAL(request->_tempFile);
|
||||||
|
}
|
||||||
if (!gzipFound){
|
if (!gzipFound){
|
||||||
|
if (_fs.exists(path)) {
|
||||||
request->_tempFile = _fs.open(path, fs::FileOpenMode::read);
|
request->_tempFile = _fs.open(path, fs::FileOpenMode::read);
|
||||||
fileFound = FILE_IS_REAL(request->_tempFile);
|
fileFound = FILE_IS_REAL(request->_tempFile);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (_fs.exists(path)) {
|
||||||
request->_tempFile = _fs.open(path, fs::FileOpenMode::read);
|
request->_tempFile = _fs.open(path, fs::FileOpenMode::read);
|
||||||
fileFound = FILE_IS_REAL(request->_tempFile);
|
fileFound = FILE_IS_REAL(request->_tempFile);
|
||||||
|
}
|
||||||
if (!fileFound){
|
if (!fileFound){
|
||||||
|
if (_fs.exists(gzip)) {
|
||||||
request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read);
|
request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read);
|
||||||
gzipFound = FILE_IS_REAL(request->_tempFile);
|
gzipFound = FILE_IS_REAL(request->_tempFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool found = fileFound || gzipFound;
|
bool found = fileFound || gzipFound;
|
||||||
|
|
||||||
@@ -198,7 +205,9 @@ void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
|
|||||||
return request->requestAuthentication();
|
return request->requestAuthentication();
|
||||||
|
|
||||||
if (request->_tempFile == true) {
|
if (request->_tempFile == true) {
|
||||||
String etag = String(request->_tempFile.size());
|
time_t lw = request->_tempFile.getLastWrite(); // get last file mod time (if supported by FS)
|
||||||
|
if (lw) setLastModified(gmtime(&lw));
|
||||||
|
String etag(lw ? lw : request->_tempFile.size()); // set etag to lastmod timestamp if available, otherwise to size
|
||||||
if (_last_modified.length() && _last_modified == request->header(F("If-Modified-Since"))) {
|
if (_last_modified.length() && _last_modified == request->header(F("If-Modified-Since"))) {
|
||||||
request->_tempFile.close();
|
request->_tempFile.close();
|
||||||
request->send(304); // Not modified
|
request->send(304); // Not modified
|
||||||
@@ -51,9 +51,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
|
|||||||
, _expectingContinue(false)
|
, _expectingContinue(false)
|
||||||
, _contentLength(0)
|
, _contentLength(0)
|
||||||
, _parsedLength(0)
|
, _parsedLength(0)
|
||||||
, _headers(LinkedList<AsyncWebHeader *>([](AsyncWebHeader *h){ delete h; }))
|
|
||||||
, _params(LinkedList<AsyncWebParameter *>([](AsyncWebParameter *p){ delete p; }))
|
, _params(LinkedList<AsyncWebParameter *>([](AsyncWebParameter *p){ delete p; }))
|
||||||
, _pathParams(LinkedList<String *>([](String *p){ delete p; }))
|
|
||||||
, _multiParseState(0)
|
, _multiParseState(0)
|
||||||
, _boundaryPosition(0)
|
, _boundaryPosition(0)
|
||||||
, _itemStartIndex(0)
|
, _itemStartIndex(0)
|
||||||
@@ -76,12 +74,12 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebServerRequest::~AsyncWebServerRequest(){
|
AsyncWebServerRequest::~AsyncWebServerRequest(){
|
||||||
_headers.free();
|
_headers.clear();
|
||||||
|
|
||||||
_params.free();
|
_params.free();
|
||||||
_pathParams.free();
|
_pathParams.clear();
|
||||||
|
|
||||||
_interestingHeaders.free();
|
_interestingHeaders.clear();
|
||||||
|
|
||||||
if(_response != NULL){
|
if(_response != NULL){
|
||||||
delete _response;
|
delete _response;
|
||||||
@@ -94,6 +92,11 @@ AsyncWebServerRequest::~AsyncWebServerRequest(){
|
|||||||
if(_tempFile){
|
if(_tempFile){
|
||||||
_tempFile.close();
|
_tempFile.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(_itemBuffer){
|
||||||
|
free(_itemBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServerRequest::_onData(void *buf, size_t len){
|
void AsyncWebServerRequest::_onData(void *buf, size_t len){
|
||||||
@@ -177,18 +180,34 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServerRequest::_removeNotInterestingHeaders(){
|
void AsyncWebServerRequest::_removeNotInterestingHeaders(){
|
||||||
if (_interestingHeaders.containsIgnoreCase(F("ANY"))) return; // nothing to do
|
if (std::any_of(std::begin(_interestingHeaders), std::end(_interestingHeaders),
|
||||||
for(const auto& header: _headers){
|
[](const String &str){ return str.equalsIgnoreCase(F("ANY")); }))
|
||||||
if(!_interestingHeaders.containsIgnoreCase(header->name().c_str())){
|
return; // nothing to do
|
||||||
_headers.remove(header);
|
|
||||||
}
|
for(auto iter = std::begin(_headers); iter != std::end(_headers); )
|
||||||
|
{
|
||||||
|
const auto name = iter->name();
|
||||||
|
|
||||||
|
if (std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders),
|
||||||
|
[&name](const String &str){ return str.equalsIgnoreCase(name); }))
|
||||||
|
iter = _headers.erase(iter);
|
||||||
|
else
|
||||||
|
iter++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServerRequest::_onPoll(){
|
void AsyncWebServerRequest::_onPoll(){
|
||||||
//os_printf("p\n");
|
//os_printf("p\n");
|
||||||
if(_response != NULL && _client != NULL && _client->canSend() && !_response->_finished()){
|
if(_response != NULL && _client != NULL && _client->canSend()){
|
||||||
|
if(!_response->_finished()){
|
||||||
_response->_ack(this, 0, 0);
|
_response->_ack(this, 0, 0);
|
||||||
|
} else {
|
||||||
|
AsyncWebServerResponse* r = _response;
|
||||||
|
_response = NULL;
|
||||||
|
delete r;
|
||||||
|
|
||||||
|
_client->close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,10 +216,12 @@ void AsyncWebServerRequest::_onAck(size_t len, uint32_t time){
|
|||||||
if(_response != NULL){
|
if(_response != NULL){
|
||||||
if(!_response->_finished()){
|
if(!_response->_finished()){
|
||||||
_response->_ack(this, len, time);
|
_response->_ack(this, len, time);
|
||||||
} else {
|
} else if(_response->_finished()){
|
||||||
AsyncWebServerResponse* r = _response;
|
AsyncWebServerResponse* r = _response;
|
||||||
_response = NULL;
|
_response = NULL;
|
||||||
delete r;
|
delete r;
|
||||||
|
|
||||||
|
_client->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,7 +253,7 @@ void AsyncWebServerRequest::_addParam(AsyncWebParameter *p){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServerRequest::_addPathParam(const char *p){
|
void AsyncWebServerRequest::_addPathParam(const char *p){
|
||||||
_pathParams.add(new String(p));
|
_pathParams.emplace_back(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServerRequest::_addGetParams(const String& params){
|
void AsyncWebServerRequest::_addGetParams(const String& params){
|
||||||
@@ -346,7 +367,7 @@ bool AsyncWebServerRequest::_parseReqHeader(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_headers.add(new AsyncWebHeader(name, value));
|
_headers.emplace_back(name, value);
|
||||||
}
|
}
|
||||||
_temp = String();
|
_temp = String();
|
||||||
return true;
|
return true;
|
||||||
@@ -587,12 +608,12 @@ void AsyncWebServerRequest::_parseLine(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncWebServerRequest::headers() const{
|
size_t AsyncWebServerRequest::headers() const{
|
||||||
return _headers.length();
|
return _headers.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncWebServerRequest::hasHeader(const String& name) const {
|
bool AsyncWebServerRequest::hasHeader(const String& name) const {
|
||||||
for(const auto& h: _headers){
|
for(const auto& h: _headers){
|
||||||
if(h->name().equalsIgnoreCase(name)){
|
if(h.name().equalsIgnoreCase(name)){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -603,22 +624,64 @@ bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper * data) const {
|
|||||||
return hasHeader(String(data));
|
return hasHeader(String(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) const {
|
AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) {
|
||||||
for(const auto& h: _headers){
|
auto iter = std::find_if(std::begin(_headers), std::end(_headers),
|
||||||
if(h->name().equalsIgnoreCase(name)){
|
[&name](const AsyncWebHeader &header){ return header.name().equalsIgnoreCase(name); });
|
||||||
return h;
|
|
||||||
|
if (iter == std::end(_headers))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return &(*iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) const {
|
||||||
|
auto iter = std::find_if(std::begin(_headers), std::end(_headers),
|
||||||
|
[&name](const AsyncWebHeader &header){ return header.name().equalsIgnoreCase(name); });
|
||||||
|
|
||||||
|
if (iter == std::end(_headers))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return &(*iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) {
|
||||||
|
PGM_P p = reinterpret_cast<PGM_P>(data);
|
||||||
|
size_t n = strlen_P(p);
|
||||||
|
char * name = (char*) malloc(n+1);
|
||||||
|
if (name) {
|
||||||
|
strcpy_P(name, p);
|
||||||
|
AsyncWebHeader* result = getHeader( String(name));
|
||||||
|
free(name);
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) const {
|
|
||||||
return getHeader(String(data));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const {
|
const AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) const {
|
||||||
auto header = _headers.nth(num);
|
PGM_P p = reinterpret_cast<PGM_P>(data);
|
||||||
return header ? *header : nullptr;
|
size_t n = strlen_P(p);
|
||||||
|
char * name = (char*) malloc(n+1);
|
||||||
|
if (name) {
|
||||||
|
strcpy_P(name, p);
|
||||||
|
const AsyncWebHeader* result = getHeader( String(name));
|
||||||
|
free(name);
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) {
|
||||||
|
if (num >= _headers.size())
|
||||||
|
return nullptr;
|
||||||
|
return &(*std::next(std::begin(_headers), num));
|
||||||
|
}
|
||||||
|
|
||||||
|
const AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const {
|
||||||
|
if (num >= _headers.size())
|
||||||
|
return nullptr;
|
||||||
|
return &(*std::next(std::begin(_headers), num));
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncWebServerRequest::params() const {
|
size_t AsyncWebServerRequest::params() const {
|
||||||
@@ -657,8 +720,9 @@ AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServerRequest::addInterestingHeader(const String& name){
|
void AsyncWebServerRequest::addInterestingHeader(const String& name){
|
||||||
if(!_interestingHeaders.containsIgnoreCase(name))
|
if(std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders),
|
||||||
_interestingHeaders.add(name);
|
[&name](const String &str){ return str.equalsIgnoreCase(name); }))
|
||||||
|
_interestingHeaders.push_back(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServerRequest::send(AsyncWebServerResponse *response){
|
void AsyncWebServerRequest::send(AsyncWebServerResponse *response){
|
||||||
@@ -850,12 +914,11 @@ const String& AsyncWebServerRequest::argName(size_t i) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const String& AsyncWebServerRequest::pathArg(size_t i) const {
|
const String& AsyncWebServerRequest::pathArg(size_t i) const {
|
||||||
auto param = _pathParams.nth(i);
|
return i < _pathParams.size() ? _pathParams[i] : emptyString;
|
||||||
return param ? **param : emptyString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const String& AsyncWebServerRequest::header(const char* name) const {
|
const String& AsyncWebServerRequest::header(const char* name) const {
|
||||||
AsyncWebHeader* h = getHeader(String(name));
|
const AsyncWebHeader* h = getHeader(String(name));
|
||||||
return h ? h->value() : emptyString;
|
return h ? h->value() : emptyString;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -865,12 +928,12 @@ const String& AsyncWebServerRequest::header(const __FlashStringHelper * data) co
|
|||||||
|
|
||||||
|
|
||||||
const String& AsyncWebServerRequest::header(size_t i) const {
|
const String& AsyncWebServerRequest::header(size_t i) const {
|
||||||
AsyncWebHeader* h = getHeader(i);
|
const AsyncWebHeader* h = getHeader(i);
|
||||||
return h ? h->value() : emptyString;
|
return h ? h->value() : emptyString;
|
||||||
}
|
}
|
||||||
|
|
||||||
const String& AsyncWebServerRequest::headerName(size_t i) const {
|
const String& AsyncWebServerRequest::headerName(size_t i) const {
|
||||||
AsyncWebHeader* h = getHeader(i);
|
const AsyncWebHeader* h = getHeader(i);
|
||||||
return h ? h->name() : emptyString;
|
return h ? h->name() : emptyString;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -898,26 +961,26 @@ String AsyncWebServerRequest::urlDecode(const String& text) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const char *AsyncWebServerRequest::methodToString() const {
|
const __FlashStringHelper *AsyncWebServerRequest::methodToString() const {
|
||||||
if(_method == HTTP_ANY) return ("ANY");
|
if(_method == HTTP_ANY) return F("ANY");
|
||||||
else if(_method & HTTP_GET) return ("GET");
|
else if(_method & HTTP_GET) return F("GET");
|
||||||
else if(_method & HTTP_POST) return ("POST");
|
else if(_method & HTTP_POST) return F("POST");
|
||||||
else if(_method & HTTP_DELETE) return ("DELETE");
|
else if(_method & HTTP_DELETE) return F("DELETE");
|
||||||
else if(_method & HTTP_PUT) return ("PUT");
|
else if(_method & HTTP_PUT) return F("PUT");
|
||||||
else if(_method & HTTP_PATCH) return ("PATCH");
|
else if(_method & HTTP_PATCH) return F("PATCH");
|
||||||
else if(_method & HTTP_HEAD) return ("HEAD");
|
else if(_method & HTTP_HEAD) return F("HEAD");
|
||||||
else if(_method & HTTP_OPTIONS) return ("OPTIONS");
|
else if(_method & HTTP_OPTIONS) return F("OPTIONS");
|
||||||
return ("UNKNOWN");
|
return F("UNKNOWN");
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *AsyncWebServerRequest::requestedConnTypeToString() const {
|
const __FlashStringHelper *AsyncWebServerRequest::requestedConnTypeToString() const {
|
||||||
switch (_reqconntype) {
|
switch (_reqconntype) {
|
||||||
case RCT_NOT_USED: return ("RCT_NOT_USED");
|
case RCT_NOT_USED: return F("RCT_NOT_USED");
|
||||||
case RCT_DEFAULT: return ("RCT_DEFAULT");
|
case RCT_DEFAULT: return F("RCT_DEFAULT");
|
||||||
case RCT_HTTP: return ("RCT_HTTP");
|
case RCT_HTTP: return F("RCT_HTTP");
|
||||||
case RCT_WS: return ("RCT_WS");
|
case RCT_WS: return F("RCT_WS");
|
||||||
case RCT_EVENT: return ("RCT_EVENT");
|
case RCT_EVENT: return F("RCT_EVENT");
|
||||||
default: return ("ERROR");
|
default: return F("ERROR");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,6 +27,8 @@
|
|||||||
#undef max
|
#undef max
|
||||||
#endif
|
#endif
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
// It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max.
|
// It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max.
|
||||||
|
|
||||||
class AsyncBasicResponse: public AsyncWebServerResponse {
|
class AsyncBasicResponse: public AsyncWebServerResponse {
|
||||||
@@ -122,7 +124,7 @@ class cbuf;
|
|||||||
|
|
||||||
class AsyncResponseStream: public AsyncAbstractResponse, public Print {
|
class AsyncResponseStream: public AsyncAbstractResponse, public Print {
|
||||||
private:
|
private:
|
||||||
cbuf *_content;
|
std::unique_ptr<cbuf> _content;
|
||||||
public:
|
public:
|
||||||
AsyncResponseStream(const String& contentType, size_t bufferSize);
|
AsyncResponseStream(const String& contentType, size_t bufferSize);
|
||||||
~AsyncResponseStream();
|
~AsyncResponseStream();
|
||||||
704
lib/ESPAsyncWebServer/src/WebResponses.cpp
Normal file
704
lib/ESPAsyncWebServer/src/WebResponses.cpp
Normal file
@@ -0,0 +1,704 @@
|
|||||||
|
/*
|
||||||
|
Asynchronous WebServer library for Espressif MCUs
|
||||||
|
|
||||||
|
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
|
||||||
|
This file is part of the esp8266 core for Arduino environment.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#include "ESPAsyncWebServer.h"
|
||||||
|
#include "WebResponseImpl.h"
|
||||||
|
#include "cbuf.h"
|
||||||
|
|
||||||
|
// Since ESP8266 does not link memchr by default, here's its implementation.
|
||||||
|
void* memchr(void* ptr, int ch, size_t count)
|
||||||
|
{
|
||||||
|
unsigned char* p = static_cast<unsigned char*>(ptr);
|
||||||
|
while(count--)
|
||||||
|
if(*p++ == static_cast<unsigned char>(ch))
|
||||||
|
return --p;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Abstract Response
|
||||||
|
* */
|
||||||
|
const char* AsyncWebServerResponse::_responseCodeToString(int code) {
|
||||||
|
return reinterpret_cast<const char *>(responseCodeToString(code));
|
||||||
|
}
|
||||||
|
|
||||||
|
const __FlashStringHelper *AsyncWebServerResponse::responseCodeToString(int code) {
|
||||||
|
switch (code) {
|
||||||
|
case 100: return F("Continue");
|
||||||
|
case 101: return F("Switching Protocols");
|
||||||
|
case 200: return F("OK");
|
||||||
|
case 201: return F("Created");
|
||||||
|
case 202: return F("Accepted");
|
||||||
|
case 203: return F("Non-Authoritative Information");
|
||||||
|
case 204: return F("No Content");
|
||||||
|
case 205: return F("Reset Content");
|
||||||
|
case 206: return F("Partial Content");
|
||||||
|
case 300: return F("Multiple Choices");
|
||||||
|
case 301: return F("Moved Permanently");
|
||||||
|
case 302: return F("Found");
|
||||||
|
case 303: return F("See Other");
|
||||||
|
case 304: return F("Not Modified");
|
||||||
|
case 305: return F("Use Proxy");
|
||||||
|
case 307: return F("Temporary Redirect");
|
||||||
|
case 400: return F("Bad Request");
|
||||||
|
case 401: return F("Unauthorized");
|
||||||
|
case 402: return F("Payment Required");
|
||||||
|
case 403: return F("Forbidden");
|
||||||
|
case 404: return F("Not Found");
|
||||||
|
case 405: return F("Method Not Allowed");
|
||||||
|
case 406: return F("Not Acceptable");
|
||||||
|
case 407: return F("Proxy Authentication Required");
|
||||||
|
case 408: return F("Request Time-out");
|
||||||
|
case 409: return F("Conflict");
|
||||||
|
case 410: return F("Gone");
|
||||||
|
case 411: return F("Length Required");
|
||||||
|
case 412: return F("Precondition Failed");
|
||||||
|
case 413: return F("Request Entity Too Large");
|
||||||
|
case 414: return F("Request-URI Too Large");
|
||||||
|
case 415: return F("Unsupported Media Type");
|
||||||
|
case 416: return F("Requested range not satisfiable");
|
||||||
|
case 417: return F("Expectation Failed");
|
||||||
|
case 500: return F("Internal Server Error");
|
||||||
|
case 501: return F("Not Implemented");
|
||||||
|
case 502: return F("Bad Gateway");
|
||||||
|
case 503: return F("Service Unavailable");
|
||||||
|
case 504: return F("Gateway Time-out");
|
||||||
|
case 505: return F("HTTP Version not supported");
|
||||||
|
default: return F("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncWebServerResponse::AsyncWebServerResponse()
|
||||||
|
: _code(0)
|
||||||
|
, _contentType()
|
||||||
|
, _contentLength(0)
|
||||||
|
, _sendContentLength(true)
|
||||||
|
, _chunked(false)
|
||||||
|
, _headLength(0)
|
||||||
|
, _sentLength(0)
|
||||||
|
, _ackedLength(0)
|
||||||
|
, _writtenLength(0)
|
||||||
|
, _state(RESPONSE_SETUP)
|
||||||
|
{
|
||||||
|
for(const auto &header: DefaultHeaders::Instance()) {
|
||||||
|
_headers.emplace_back(header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncWebServerResponse::~AsyncWebServerResponse() = default;
|
||||||
|
|
||||||
|
void AsyncWebServerResponse::setCode(int code){
|
||||||
|
if(_state == RESPONSE_SETUP)
|
||||||
|
_code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerResponse::setContentLength(size_t len){
|
||||||
|
if(_state == RESPONSE_SETUP)
|
||||||
|
_contentLength = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerResponse::setContentType(const String& type){
|
||||||
|
if(_state == RESPONSE_SETUP)
|
||||||
|
_contentType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerResponse::addHeader(const String& name, const String& value){
|
||||||
|
_headers.emplace_back(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
String AsyncWebServerResponse::_assembleHead(uint8_t version){
|
||||||
|
if(version){
|
||||||
|
addHeader(F("Accept-Ranges"), F("none"));
|
||||||
|
if(_chunked)
|
||||||
|
addHeader(F("Transfer-Encoding"), F("chunked"));
|
||||||
|
}
|
||||||
|
String out = String();
|
||||||
|
int bufSize = 300;
|
||||||
|
char buf[bufSize];
|
||||||
|
|
||||||
|
snprintf_P(buf, bufSize, PSTR("HTTP/1.%d %d %s\r\n"), version, _code, _responseCodeToString(_code));
|
||||||
|
out.concat(buf);
|
||||||
|
|
||||||
|
if(_sendContentLength) {
|
||||||
|
snprintf_P(buf, bufSize, PSTR("Content-Length: %d\r\n"), _contentLength);
|
||||||
|
out.concat(buf);
|
||||||
|
}
|
||||||
|
if(_contentType.length()) {
|
||||||
|
snprintf_P(buf, bufSize, PSTR("Content-Type: %s\r\n"), _contentType.c_str());
|
||||||
|
out.concat(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const auto& header: _headers){
|
||||||
|
snprintf_P(buf, bufSize, PSTR("%s: %s\r\n"), header.name().c_str(), header.value().c_str());
|
||||||
|
out.concat(buf);
|
||||||
|
}
|
||||||
|
_headers.clear();
|
||||||
|
|
||||||
|
out.concat(F("\r\n"));
|
||||||
|
_headLength = out.length();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; }
|
||||||
|
bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; }
|
||||||
|
bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; }
|
||||||
|
bool AsyncWebServerResponse::_sourceValid() const { return false; }
|
||||||
|
void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request){ _state = RESPONSE_END; request->client()->close(); }
|
||||||
|
size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ (void)request; (void)len; (void)time; return 0; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* String/Code Response
|
||||||
|
* */
|
||||||
|
AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const String& content){
|
||||||
|
_code = code;
|
||||||
|
_content = content;
|
||||||
|
_contentType = contentType;
|
||||||
|
if(_content.length()){
|
||||||
|
_contentLength = _content.length();
|
||||||
|
if(!_contentType.length())
|
||||||
|
_contentType = F("text/plain");
|
||||||
|
}
|
||||||
|
addHeader(F("Connection"), F("close"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){
|
||||||
|
_state = RESPONSE_HEADERS;
|
||||||
|
String out = _assembleHead(request->version());
|
||||||
|
size_t outLen = out.length();
|
||||||
|
size_t space = request->client()->space();
|
||||||
|
if(!_contentLength && space >= outLen){
|
||||||
|
_writtenLength += request->client()->write(out.c_str(), outLen);
|
||||||
|
_state = RESPONSE_WAIT_ACK;
|
||||||
|
} else if(_contentLength && space >= outLen + _contentLength){
|
||||||
|
out += _content;
|
||||||
|
outLen += _contentLength;
|
||||||
|
_writtenLength += request->client()->write(out.c_str(), outLen);
|
||||||
|
_state = RESPONSE_WAIT_ACK;
|
||||||
|
} else if(space && space < outLen){
|
||||||
|
String partial = out.substring(0, space);
|
||||||
|
_content = out.substring(space) + _content;
|
||||||
|
_contentLength += outLen - space;
|
||||||
|
_writtenLength += request->client()->write(partial.c_str(), partial.length());
|
||||||
|
_state = RESPONSE_CONTENT;
|
||||||
|
} else if(space > outLen && space < (outLen + _contentLength)){
|
||||||
|
size_t shift = space - outLen;
|
||||||
|
outLen += shift;
|
||||||
|
_sentLength += shift;
|
||||||
|
out += _content.substring(0, shift);
|
||||||
|
_content = _content.substring(shift);
|
||||||
|
_writtenLength += request->client()->write(out.c_str(), outLen);
|
||||||
|
_state = RESPONSE_CONTENT;
|
||||||
|
} else {
|
||||||
|
_content = out + _content;
|
||||||
|
_contentLength += outLen;
|
||||||
|
_state = RESPONSE_CONTENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
|
||||||
|
(void)time;
|
||||||
|
_ackedLength += len;
|
||||||
|
if(_state == RESPONSE_CONTENT){
|
||||||
|
size_t available = _contentLength - _sentLength;
|
||||||
|
size_t space = request->client()->space();
|
||||||
|
//we can fit in this packet
|
||||||
|
if(space > available){
|
||||||
|
_writtenLength += request->client()->write(_content.c_str(), available);
|
||||||
|
_content = String();
|
||||||
|
_state = RESPONSE_WAIT_ACK;
|
||||||
|
return available;
|
||||||
|
}
|
||||||
|
//send some data, the rest on ack
|
||||||
|
String out = _content.substring(0, space);
|
||||||
|
_content = _content.substring(space);
|
||||||
|
_sentLength += space;
|
||||||
|
_writtenLength += request->client()->write(out.c_str(), space);
|
||||||
|
return space;
|
||||||
|
} else if(_state == RESPONSE_WAIT_ACK){
|
||||||
|
if(_ackedLength >= _writtenLength){
|
||||||
|
_state = RESPONSE_END;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Abstract Response
|
||||||
|
* */
|
||||||
|
|
||||||
|
AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback): _callback(callback)
|
||||||
|
{
|
||||||
|
// In case of template processing, we're unable to determine real response size
|
||||||
|
if(callback) {
|
||||||
|
_contentLength = 0;
|
||||||
|
_sendContentLength = false;
|
||||||
|
_chunked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){
|
||||||
|
addHeader(F("Connection"), F("close"));
|
||||||
|
_head = _assembleHead(request->version());
|
||||||
|
_state = RESPONSE_HEADERS;
|
||||||
|
_ack(request, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
|
||||||
|
(void)time;
|
||||||
|
if(!_sourceValid()){
|
||||||
|
_state = RESPONSE_FAILED;
|
||||||
|
request->client()->close();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_ackedLength += len;
|
||||||
|
size_t space = request->client()->space();
|
||||||
|
|
||||||
|
size_t headLen = _head.length();
|
||||||
|
if(_state == RESPONSE_HEADERS){
|
||||||
|
if(space >= headLen){
|
||||||
|
_state = RESPONSE_CONTENT;
|
||||||
|
space -= headLen;
|
||||||
|
} else {
|
||||||
|
String out = _head.substring(0, space);
|
||||||
|
_head = _head.substring(space);
|
||||||
|
_writtenLength += request->client()->write(out.c_str(), out.length());
|
||||||
|
return out.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_state == RESPONSE_CONTENT){
|
||||||
|
size_t outLen;
|
||||||
|
if(_chunked){
|
||||||
|
if(space <= 8){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
outLen = space;
|
||||||
|
} else if(!_sendContentLength){
|
||||||
|
outLen = space;
|
||||||
|
} else {
|
||||||
|
outLen = ((_contentLength - _sentLength) > space)?space:(_contentLength - _sentLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *buf = (uint8_t *)malloc(outLen+headLen);
|
||||||
|
if (!buf) {
|
||||||
|
// os_printf("_ack malloc %d failed\n", outLen+headLen);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(headLen){
|
||||||
|
memcpy(buf, _head.c_str(), _head.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t readLen = 0;
|
||||||
|
|
||||||
|
if(_chunked){
|
||||||
|
// 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_P((char*)buf+headLen, PSTR("%x"), readLen) + headLen;
|
||||||
|
while(outLen < headLen + 4) buf[outLen++] = ' ';
|
||||||
|
buf[outLen++] = '\r';
|
||||||
|
buf[outLen++] = '\n';
|
||||||
|
outLen += readLen;
|
||||||
|
buf[outLen++] = '\r';
|
||||||
|
buf[outLen++] = '\n';
|
||||||
|
} else {
|
||||||
|
readLen = _fillBufferAndProcessTemplates(buf+headLen, outLen);
|
||||||
|
if(readLen == RESPONSE_TRY_AGAIN){
|
||||||
|
free(buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
outLen = readLen + headLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(headLen){
|
||||||
|
_head = String();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(outLen){
|
||||||
|
_writtenLength += request->client()->write((const char*)buf, outLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_chunked){
|
||||||
|
_sentLength += readLen;
|
||||||
|
} else {
|
||||||
|
_sentLength += outLen - headLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
if((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)){
|
||||||
|
_state = RESPONSE_WAIT_ACK;
|
||||||
|
}
|
||||||
|
return outLen;
|
||||||
|
|
||||||
|
} else if(_state == RESPONSE_WAIT_ACK){
|
||||||
|
if(!_sendContentLength || _ackedLength >= _writtenLength){
|
||||||
|
_state = RESPONSE_END;
|
||||||
|
if(!_chunked && !_sendContentLength)
|
||||||
|
request->client()->close(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len)
|
||||||
|
{
|
||||||
|
// If we have something in cache, copy it to buffer
|
||||||
|
const size_t readFromCache = std::min(len, _cache.size());
|
||||||
|
if(readFromCache) {
|
||||||
|
memcpy(data, _cache.data(), readFromCache);
|
||||||
|
_cache.erase(_cache.begin(), _cache.begin() + readFromCache);
|
||||||
|
}
|
||||||
|
// If we need to read more...
|
||||||
|
const size_t needFromFile = len - readFromCache;
|
||||||
|
const size_t readFromContent = _fillBuffer(data + readFromCache, needFromFile);
|
||||||
|
return readFromCache + readFromContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len)
|
||||||
|
{
|
||||||
|
if(!_callback)
|
||||||
|
return _fillBuffer(data, len);
|
||||||
|
|
||||||
|
const size_t originalLen = len;
|
||||||
|
len = _readDataFromCacheOrContent(data, len);
|
||||||
|
// Now we've read 'len' bytes, either from cache or from file
|
||||||
|
// Search for template placeholders
|
||||||
|
uint8_t* pTemplateStart = data;
|
||||||
|
while((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t*)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1]
|
||||||
|
uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr;
|
||||||
|
// temporary buffer to hold parameter name
|
||||||
|
uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1];
|
||||||
|
String paramName;
|
||||||
|
// If closing placeholder is found:
|
||||||
|
if(pTemplateEnd) {
|
||||||
|
// prepare argument to callback
|
||||||
|
const size_t paramNameLength = std::min((size_t)sizeof(buf) - 1, (size_t)(pTemplateEnd - pTemplateStart - 1));
|
||||||
|
if(paramNameLength) {
|
||||||
|
memcpy(buf, pTemplateStart + 1, paramNameLength);
|
||||||
|
buf[paramNameLength] = 0;
|
||||||
|
paramName = String(reinterpret_cast<char*>(buf));
|
||||||
|
} else { // double percent sign encountered, this is single percent sign escaped.
|
||||||
|
// remove the 2nd percent sign
|
||||||
|
memmove(pTemplateEnd, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
|
||||||
|
len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1;
|
||||||
|
++pTemplateStart;
|
||||||
|
}
|
||||||
|
} else if(&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data
|
||||||
|
memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart);
|
||||||
|
const size_t readFromCacheOrContent = _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1));
|
||||||
|
if(readFromCacheOrContent) {
|
||||||
|
pTemplateEnd = (uint8_t*)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent);
|
||||||
|
if(pTemplateEnd) {
|
||||||
|
// prepare argument to callback
|
||||||
|
*pTemplateEnd = 0;
|
||||||
|
paramName = String(reinterpret_cast<char*>(buf));
|
||||||
|
// 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.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
|
||||||
|
++pTemplateStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
|
||||||
|
++pTemplateStart;
|
||||||
|
}
|
||||||
|
else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
|
||||||
|
++pTemplateStart;
|
||||||
|
if(paramName.length()) {
|
||||||
|
// call callback and replace with result.
|
||||||
|
// Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value.
|
||||||
|
// Data after pTemplateEnd may need to be moved.
|
||||||
|
// The first byte of data after placeholder is located at pTemplateEnd + 1.
|
||||||
|
// It should be located at pTemplateStart + numBytesCopied (to begin right after inserted parameter value).
|
||||||
|
const String paramValue(_callback(paramName));
|
||||||
|
const char* pvstr = paramValue.c_str();
|
||||||
|
const unsigned int pvlen = paramValue.length();
|
||||||
|
const size_t numBytesCopied = std::min(pvlen, static_cast<unsigned int>(&data[originalLen - 1] - pTemplateStart + 1));
|
||||||
|
// 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)) {
|
||||||
|
_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);
|
||||||
|
len = originalLen; // fix issue with truncated data, not sure if it has any side effects
|
||||||
|
} else if(pTemplateEnd + 1 != pTemplateStart + numBytesCopied)
|
||||||
|
//2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit.
|
||||||
|
// Move the entire data after the placeholder
|
||||||
|
memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
|
||||||
|
// 3. replace placeholder with actual value
|
||||||
|
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(_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;
|
||||||
|
const size_t totalFreeRoom = originalLen - len + roomFreed;
|
||||||
|
len += _readDataFromCacheOrContent(&data[len - roomFreed], totalFreeRoom) - roomFreed;
|
||||||
|
} else { // result is copied fully; it is longer than placeholder text
|
||||||
|
const size_t roomTaken = pTemplateStart + numBytesCopied - pTemplateEnd - 1;
|
||||||
|
len = std::min(len + roomTaken, originalLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // while(pTemplateStart)
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* File Response
|
||||||
|
* */
|
||||||
|
|
||||||
|
AsyncFileResponse::~AsyncFileResponse(){
|
||||||
|
if(_content)
|
||||||
|
_content.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncFileResponse::_setContentType(const String& path){
|
||||||
|
#if HAVE_EXTERN_GET_CONTENT_TYPE_FUNCTION
|
||||||
|
extern const __FlashStringHelper *getContentType(const String &path);
|
||||||
|
_contentType = getContentType(path);
|
||||||
|
#else
|
||||||
|
if (path.endsWith(F(".html"))) _contentType = F("text/html");
|
||||||
|
else if (path.endsWith(F(".htm"))) _contentType = F("text/html");
|
||||||
|
else if (path.endsWith(F(".css"))) _contentType = F("text/css");
|
||||||
|
else if (path.endsWith(F(".json"))) _contentType = F("application/json");
|
||||||
|
else if (path.endsWith(F(".js"))) _contentType = F("application/javascript");
|
||||||
|
else if (path.endsWith(F(".png"))) _contentType = F("image/png");
|
||||||
|
else if (path.endsWith(F(".gif"))) _contentType = F("image/gif");
|
||||||
|
else if (path.endsWith(F(".jpg"))) _contentType = F("image/jpeg");
|
||||||
|
else if (path.endsWith(F(".ico"))) _contentType = F("image/x-icon");
|
||||||
|
else if (path.endsWith(F(".svg"))) _contentType = F("image/svg+xml");
|
||||||
|
else if (path.endsWith(F(".eot"))) _contentType = F("font/eot");
|
||||||
|
else if (path.endsWith(F(".woff"))) _contentType = F("font/woff");
|
||||||
|
else if (path.endsWith(F(".woff2"))) _contentType = F("font/woff2");
|
||||||
|
else if (path.endsWith(F(".ttf"))) _contentType = F("font/ttf");
|
||||||
|
else if (path.endsWith(F(".xml"))) _contentType = F("text/xml");
|
||||||
|
else if (path.endsWith(F(".pdf"))) _contentType = F("application/pdf");
|
||||||
|
else if (path.endsWith(F(".zip"))) _contentType = F("application/zip");
|
||||||
|
else if(path.endsWith(F(".gz"))) _contentType = F("application/x-gzip");
|
||||||
|
else _contentType = F("text/plain");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){
|
||||||
|
_code = 200;
|
||||||
|
_path = path;
|
||||||
|
|
||||||
|
if(!download && !fs.exists(_path) && fs.exists(_path + F(".gz"))){
|
||||||
|
_path = _path + F(".gz");
|
||||||
|
addHeader(F("Content-Encoding"), F("gzip"));
|
||||||
|
_callback = nullptr; // Unable to process zipped templates
|
||||||
|
_sendContentLength = true;
|
||||||
|
_chunked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_content = fs.open(_path, fs::FileOpenMode::read);
|
||||||
|
_contentLength = _content.size();
|
||||||
|
|
||||||
|
if(contentType.length() == 0)
|
||||||
|
_setContentType(path);
|
||||||
|
else
|
||||||
|
_contentType = contentType;
|
||||||
|
|
||||||
|
int filenameStart = path.lastIndexOf('/') + 1;
|
||||||
|
char buf[26+path.length()-filenameStart];
|
||||||
|
char* filename = (char*)path.c_str() + filenameStart;
|
||||||
|
|
||||||
|
if(download) {
|
||||||
|
// set filename and force download
|
||||||
|
snprintf_P(buf, sizeof (buf), PSTR("attachment; filename=\"%s\""), filename);
|
||||||
|
} else {
|
||||||
|
// set filename and force rendering
|
||||||
|
snprintf_P(buf, sizeof (buf), PSTR("inline; filename=\"%s\""), filename);
|
||||||
|
}
|
||||||
|
addHeader(F("Content-Disposition"), buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFileResponse::AsyncFileResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){
|
||||||
|
_code = 200;
|
||||||
|
_path = path;
|
||||||
|
|
||||||
|
if(!download && String(content.name()).endsWith(F(".gz")) && !path.endsWith(F(".gz"))){
|
||||||
|
addHeader(F("Content-Encoding"), F("gzip"));
|
||||||
|
_callback = nullptr; // Unable to process gzipped templates
|
||||||
|
_sendContentLength = true;
|
||||||
|
_chunked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_content = content;
|
||||||
|
_contentLength = _content.size();
|
||||||
|
|
||||||
|
if(contentType.length() == 0)
|
||||||
|
_setContentType(path);
|
||||||
|
else
|
||||||
|
_contentType = contentType;
|
||||||
|
|
||||||
|
int filenameStart = path.lastIndexOf('/') + 1;
|
||||||
|
char buf[26+path.length()-filenameStart];
|
||||||
|
char* filename = (char*)path.c_str() + filenameStart;
|
||||||
|
|
||||||
|
if(download) {
|
||||||
|
snprintf_P(buf, sizeof (buf), PSTR("attachment; filename=\"%s\""), filename);
|
||||||
|
} else {
|
||||||
|
snprintf_P(buf, sizeof (buf), PSTR("inline; filename=\"%s\""), filename);
|
||||||
|
}
|
||||||
|
addHeader(F("Content-Disposition"), buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){
|
||||||
|
return _content.read(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stream Response
|
||||||
|
* */
|
||||||
|
|
||||||
|
AsyncStreamResponse::AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) {
|
||||||
|
_code = 200;
|
||||||
|
_content = &stream;
|
||||||
|
_contentLength = len;
|
||||||
|
_contentType = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len){
|
||||||
|
size_t available = _content->available();
|
||||||
|
size_t outLen = (available > len)?len:available;
|
||||||
|
size_t i;
|
||||||
|
for(i=0;i<outLen;i++)
|
||||||
|
data[i] = _content->read();
|
||||||
|
return outLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback Response
|
||||||
|
* */
|
||||||
|
|
||||||
|
AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback): AsyncAbstractResponse(templateCallback) {
|
||||||
|
_code = 200;
|
||||||
|
_content = callback;
|
||||||
|
_contentLength = len;
|
||||||
|
if(!len)
|
||||||
|
_sendContentLength = false;
|
||||||
|
_contentType = contentType;
|
||||||
|
_filledLength = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chunked Response
|
||||||
|
* */
|
||||||
|
|
||||||
|
AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback): AsyncAbstractResponse(processorCallback) {
|
||||||
|
_code = 200;
|
||||||
|
_content = callback;
|
||||||
|
_contentLength = 0;
|
||||||
|
_contentType = contentType;
|
||||||
|
_sendContentLength = false;
|
||||||
|
_chunked = true;
|
||||||
|
_filledLength = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Progmem Response
|
||||||
|
* */
|
||||||
|
|
||||||
|
AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) {
|
||||||
|
_code = code;
|
||||||
|
_content = content;
|
||||||
|
_contentType = contentType;
|
||||||
|
_contentLength = len;
|
||||||
|
_readLength = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){
|
||||||
|
size_t left = _contentLength - _readLength;
|
||||||
|
if (left > len) {
|
||||||
|
memcpy_P(data, _content + _readLength, len);
|
||||||
|
_readLength += len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
memcpy_P(data, _content + _readLength, left);
|
||||||
|
_readLength += left;
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Response Stream (You can print/write/printf to it, up to the contentLen bytes)
|
||||||
|
* */
|
||||||
|
|
||||||
|
AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize)
|
||||||
|
{
|
||||||
|
_code = 200;
|
||||||
|
_contentLength = 0;
|
||||||
|
_contentType = contentType;
|
||||||
|
_content = std::unique_ptr<cbuf>(new cbuf(bufferSize)); //std::make_unique<cbuf>(bufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncResponseStream::~AsyncResponseStream() = default;
|
||||||
|
|
||||||
|
size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen){
|
||||||
|
return _content->read((char*)buf, maxLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AsyncResponseStream::write(const uint8_t *data, size_t len){
|
||||||
|
if(_started())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if(len > _content->room()){
|
||||||
|
size_t needed = len - _content->room();
|
||||||
|
_content->resizeAdd(needed);
|
||||||
|
}
|
||||||
|
size_t written = _content->write((const char*)data, len);
|
||||||
|
_contentLength += written;
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AsyncResponseStream::write(uint8_t data){
|
||||||
|
return write(&data, 1);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user