Webovy server odsud https://github.com/vortigont/ESPAsyncWebServer/tree/yubxmod - vsechny upravy a zpristupneni bufferu pro JSON dle https://github.com/yubox-node-org/ESPAsyncWebServer/pull/8
This commit is contained in:
@ -21,24 +21,24 @@
|
||||
#include "AsyncEventSource.h"
|
||||
|
||||
static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect){
|
||||
String ev = "";
|
||||
String ev;
|
||||
|
||||
if(reconnect){
|
||||
ev += "retry: ";
|
||||
ev += String(reconnect);
|
||||
ev += "\r\n";
|
||||
ev += F("retry: ");
|
||||
ev += reconnect;
|
||||
ev += F("\r\n");
|
||||
}
|
||||
|
||||
if(id){
|
||||
ev += "id: ";
|
||||
ev += F("id: ");
|
||||
ev += String(id);
|
||||
ev += "\r\n";
|
||||
ev += F("\r\n");
|
||||
}
|
||||
|
||||
if(event != NULL){
|
||||
ev += "event: ";
|
||||
ev += F("event: ");
|
||||
ev += String(event);
|
||||
ev += "\r\n";
|
||||
ev += F("\r\n");
|
||||
}
|
||||
|
||||
if(message != NULL){
|
||||
@ -54,9 +54,9 @@ static String generateEventMessage(const char *message, const char *event, uint3
|
||||
if(ldata != NULL){
|
||||
memcpy(ldata, lineStart, llen);
|
||||
ldata[llen] = 0;
|
||||
ev += "data: ";
|
||||
ev += F("data: ");
|
||||
ev += ldata;
|
||||
ev += "\r\n\r\n";
|
||||
ev += F("\r\n\r\n");
|
||||
free(ldata);
|
||||
}
|
||||
lineStart = (char *)message + messageLen;
|
||||
@ -89,14 +89,14 @@ static String generateEventMessage(const char *message, const char *event, uint3
|
||||
if(ldata != NULL){
|
||||
memcpy(ldata, lineStart, llen);
|
||||
ldata[llen] = 0;
|
||||
ev += "data: ";
|
||||
ev += F("data: ");
|
||||
ev += ldata;
|
||||
ev += "\r\n";
|
||||
ev += F("\r\n");
|
||||
free(ldata);
|
||||
}
|
||||
lineStart = nextLine;
|
||||
if(lineStart == ((char *)message + messageLen))
|
||||
ev += "\r\n";
|
||||
ev += F("\r\n");
|
||||
}
|
||||
} while(lineStart < ((char *)message + messageLen));
|
||||
}
|
||||
@ -137,17 +137,17 @@ size_t AsyncEventSourceMessage::ack(size_t len, uint32_t time) {
|
||||
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) {
|
||||
if (!client->canSend())
|
||||
return 0;
|
||||
const size_t len = _len - _sent;
|
||||
if(client->space() < len){
|
||||
return 0;
|
||||
}
|
||||
size_t sent = client->add((const char *)_data, len);
|
||||
client->send();
|
||||
_sent += sent;
|
||||
return sent;
|
||||
if (_sent >= _len) {
|
||||
return 0;
|
||||
}
|
||||
const size_t len_to_send = _len - _sent;
|
||||
auto position = reinterpret_cast<const char*>(_data + _sent);
|
||||
const size_t sent_now = client->write(position, len_to_send);
|
||||
_sent += sent_now;
|
||||
return sent_now;
|
||||
}
|
||||
|
||||
// Client
|
||||
@ -158,8 +158,8 @@ AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, A
|
||||
_client = request->client();
|
||||
_server = server;
|
||||
_lastId = 0;
|
||||
if(request->hasHeader("Last-Event-ID"))
|
||||
_lastId = atoi(request->getHeader("Last-Event-ID")->value().c_str());
|
||||
if(request->hasHeader(F("Last-Event-ID")))
|
||||
_lastId = atoi(request->getHeader(F("Last-Event-ID"))->value().c_str());
|
||||
|
||||
_client->setRxTimeout(0);
|
||||
_client->onError(NULL, NULL);
|
||||
@ -174,7 +174,9 @@ AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, A
|
||||
}
|
||||
|
||||
AsyncEventSourceClient::~AsyncEventSourceClient(){
|
||||
_messageQueue.free();
|
||||
_lockmq.lock();
|
||||
_messageQueue.free();
|
||||
_lockmq.unlock();
|
||||
close();
|
||||
}
|
||||
|
||||
@ -185,33 +187,41 @@ void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage)
|
||||
delete dataMessage;
|
||||
return;
|
||||
}
|
||||
//length() is not thread-safe, thus acquiring the lock before this call..
|
||||
_lockmq.lock();
|
||||
if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){
|
||||
ets_printf("ERROR: Too many messages queued\n");
|
||||
ets_printf(String(F("ERROR: Too many messages queued\n")).c_str());
|
||||
delete dataMessage;
|
||||
} else {
|
||||
_messageQueue.add(dataMessage);
|
||||
_messageQueue.add(dataMessage);
|
||||
// runqueue trigger when new messages added
|
||||
if(_client->canSend()) {
|
||||
_runQueue();
|
||||
}
|
||||
}
|
||||
if(_client->canSend())
|
||||
_runQueue();
|
||||
_lockmq.unlock();
|
||||
}
|
||||
|
||||
void AsyncEventSourceClient::_onAck(size_t len, uint32_t time){
|
||||
// Same here, acquiring the lock early
|
||||
_lockmq.lock();
|
||||
while(len && !_messageQueue.isEmpty()){
|
||||
len = _messageQueue.front()->ack(len, time);
|
||||
if(_messageQueue.front()->finished())
|
||||
_messageQueue.remove(_messageQueue.front());
|
||||
}
|
||||
|
||||
_runQueue();
|
||||
_lockmq.unlock();
|
||||
}
|
||||
|
||||
void AsyncEventSourceClient::_onPoll(){
|
||||
_lockmq.lock();
|
||||
if(!_messageQueue.isEmpty()){
|
||||
_runQueue();
|
||||
}
|
||||
_lockmq.unlock();
|
||||
}
|
||||
|
||||
|
||||
void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))){
|
||||
_client->close(true);
|
||||
}
|
||||
@ -226,7 +236,7 @@ void AsyncEventSourceClient::close(){
|
||||
_client->close();
|
||||
}
|
||||
|
||||
void AsyncEventSourceClient::write(const char * message, size_t len){
|
||||
void AsyncEventSourceClient::_write(const char * message, size_t len){
|
||||
_queueMessage(new AsyncEventSourceMessage(message, len));
|
||||
}
|
||||
|
||||
@ -235,15 +245,23 @@ void AsyncEventSourceClient::send(const char *message, const char *event, uint32
|
||||
_queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length()));
|
||||
}
|
||||
|
||||
void AsyncEventSourceClient::_runQueue(){
|
||||
while(!_messageQueue.isEmpty() && _messageQueue.front()->finished()){
|
||||
_messageQueue.remove(_messageQueue.front());
|
||||
}
|
||||
size_t AsyncEventSourceClient::packetsWaiting() const {
|
||||
size_t len;
|
||||
_lockmq.lock();
|
||||
len = _messageQueue.length();
|
||||
_lockmq.unlock();
|
||||
return len;
|
||||
}
|
||||
|
||||
for(auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i)
|
||||
{
|
||||
if(!(*i)->sent())
|
||||
void AsyncEventSourceClient::_runQueue() {
|
||||
// Calls to this private method now already protected by _lockmq acquisition
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,6 +282,10 @@ void AsyncEventSource::onConnect(ArEventHandlerFunction cb){
|
||||
_connectcb = cb;
|
||||
}
|
||||
|
||||
void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb){
|
||||
_authorizeConnectHandler = cb;
|
||||
}
|
||||
|
||||
void AsyncEventSource::_addClient(AsyncEventSourceClient * client){
|
||||
/*char * temp = (char *)malloc(2054);
|
||||
if(temp != NULL){
|
||||
@ -277,17 +299,22 @@ void AsyncEventSource::_addClient(AsyncEventSourceClient * client){
|
||||
client->write((const char *)temp, 2053);
|
||||
free(temp);
|
||||
}*/
|
||||
|
||||
AsyncWebLockGuard l(_client_queue_lock);
|
||||
_clients.add(client);
|
||||
if(_connectcb)
|
||||
_connectcb(client);
|
||||
}
|
||||
|
||||
void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client){
|
||||
AsyncWebLockGuard l(_client_queue_lock);
|
||||
_clients.remove(client);
|
||||
}
|
||||
|
||||
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){
|
||||
if(c->connected())
|
||||
c->close();
|
||||
@ -296,50 +323,59 @@ void AsyncEventSource::close(){
|
||||
|
||||
// pmb fix
|
||||
size_t AsyncEventSource::avgPacketsWaiting() const {
|
||||
if(_clients.isEmpty())
|
||||
size_t aql = 0;
|
||||
uint32_t nConnectedClients = 0;
|
||||
AsyncWebLockGuard l(_client_queue_lock);
|
||||
if (_clients.isEmpty()) {
|
||||
return 0;
|
||||
|
||||
size_t aql=0;
|
||||
uint32_t nConnectedClients=0;
|
||||
|
||||
}
|
||||
for(const auto &c: _clients){
|
||||
if(c->connected()) {
|
||||
aql+=c->packetsWaiting();
|
||||
aql += c->packetsWaiting();
|
||||
++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);
|
||||
AsyncWebLockGuard l(_client_queue_lock);
|
||||
for(const auto &c: _clients){
|
||||
if(c->connected()) {
|
||||
c->write(ev.c_str(), ev.length());
|
||||
c->_write(ev.c_str(), ev.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t AsyncEventSource::count() const {
|
||||
return _clients.count_if([](AsyncEventSourceClient *c){
|
||||
return c->connected();
|
||||
});
|
||||
size_t n_clients;
|
||||
AsyncWebLockGuard l(_client_queue_lock);
|
||||
n_clients = _clients.count_if([](AsyncEventSourceClient *c){
|
||||
return c->connected();
|
||||
});
|
||||
return n_clients;
|
||||
}
|
||||
|
||||
bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){
|
||||
if(request->method() != HTTP_GET || !request->url().equals(_url)) {
|
||||
return false;
|
||||
}
|
||||
request->addInterestingHeader("Last-Event-ID");
|
||||
request->addInterestingHeader(F("Last-Event-ID"));
|
||||
request->addInterestingHeader("Cookie");
|
||||
return true;
|
||||
}
|
||||
|
||||
void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){
|
||||
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
||||
if((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) {
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
if(_authorizeConnectHandler != NULL){
|
||||
if(!_authorizeConnectHandler(request)){
|
||||
return request->send(401);
|
||||
}
|
||||
}
|
||||
request->send(new AsyncEventSourceResponse(this));
|
||||
}
|
||||
|
||||
@ -348,10 +384,10 @@ void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){
|
||||
AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server){
|
||||
_server = server;
|
||||
_code = 200;
|
||||
_contentType = "text/event-stream";
|
||||
_contentType = F("text/event-stream");
|
||||
_sendContentLength = false;
|
||||
addHeader("Cache-Control", "no-cache");
|
||||
addHeader("Connection","keep-alive");
|
||||
addHeader(F("Cache-Control"), F("no-cache"));
|
||||
addHeader(F("Connection"), F("keep-alive"));
|
||||
}
|
||||
|
||||
void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request){
|
||||
|
@ -21,17 +21,13 @@
|
||||
#define ASYNCEVENTSOURCE_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETUYA)
|
||||
#ifdef ESP32
|
||||
#include <AsyncTCP.h>
|
||||
#define SSE_MAX_QUEUED_MESSAGES 32
|
||||
#else
|
||||
#include <ESPAsyncTCP.h>
|
||||
#define SSE_MAX_QUEUED_MESSAGES 8
|
||||
#endif
|
||||
|
||||
#ifndef SSE_MAX_QUEUED_MESSAGES
|
||||
#define SSE_MAX_QUEUED_MESSAGES 32
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#include "AsyncWebSynchronization.h"
|
||||
@ -43,7 +39,7 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ESP32) || defined(LIBRETUYA)
|
||||
#ifdef ESP32
|
||||
#define DEFAULT_MAX_SSE_CLIENTS 8
|
||||
#else
|
||||
#define DEFAULT_MAX_SSE_CLIENTS 4
|
||||
@ -53,14 +49,15 @@ class AsyncEventSource;
|
||||
class AsyncEventSourceResponse;
|
||||
class AsyncEventSourceClient;
|
||||
typedef std::function<void(AsyncEventSourceClient *client)> ArEventHandlerFunction;
|
||||
typedef std::function<bool(AsyncWebServerRequest *request)> ArAuthorizeConnectHandler;
|
||||
|
||||
class AsyncEventSourceMessage {
|
||||
private:
|
||||
uint8_t * _data;
|
||||
uint8_t * _data;
|
||||
size_t _len;
|
||||
size_t _sent;
|
||||
//size_t _ack;
|
||||
size_t _acked;
|
||||
size_t _acked;
|
||||
public:
|
||||
AsyncEventSourceMessage(const char * data, size_t len);
|
||||
~AsyncEventSourceMessage();
|
||||
@ -76,6 +73,8 @@ class AsyncEventSourceClient {
|
||||
AsyncEventSource *_server;
|
||||
uint32_t _lastId;
|
||||
LinkedList<AsyncEventSourceMessage *> _messageQueue;
|
||||
// ArFi 2020-08-27 for protecting/serializing _messageQueue
|
||||
AsyncPlainLock _lockmq;
|
||||
void _queueMessage(AsyncEventSourceMessage *dataMessage);
|
||||
void _runQueue();
|
||||
|
||||
@ -86,15 +85,15 @@ class AsyncEventSourceClient {
|
||||
|
||||
AsyncClient* client(){ return _client; }
|
||||
void close();
|
||||
void write(const char * message, size_t len);
|
||||
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(); }
|
||||
uint32_t lastId() const { return _lastId; }
|
||||
size_t packetsWaiting() const { return _messageQueue.length(); }
|
||||
size_t packetsWaiting() const;
|
||||
|
||||
void _write(const char * message, size_t len);
|
||||
//system callbacks (do not call)
|
||||
void _onAck(size_t len, uint32_t time);
|
||||
void _onPoll();
|
||||
void _onPoll();
|
||||
void _onTimeout(uint32_t time);
|
||||
void _onDisconnect();
|
||||
};
|
||||
@ -103,7 +102,11 @@ class AsyncEventSource: public AsyncWebHandler {
|
||||
private:
|
||||
String _url;
|
||||
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;
|
||||
ArAuthorizeConnectHandler _authorizeConnectHandler;
|
||||
public:
|
||||
AsyncEventSource(const String& url);
|
||||
~AsyncEventSource();
|
||||
@ -111,8 +114,9 @@ class AsyncEventSource: public AsyncWebHandler {
|
||||
const char * url() const { return _url.c_str(); }
|
||||
void close();
|
||||
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);
|
||||
size_t count() const; //number clinets connected
|
||||
size_t count() const; //number clients connected
|
||||
size_t avgPacketsWaiting() const;
|
||||
|
||||
//system callbacks (do not call)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -22,9 +22,9 @@
|
||||
#define ASYNCWEBSOCKET_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETUYA)
|
||||
#ifdef ESP32
|
||||
#include <AsyncTCP.h>
|
||||
#define WS_MAX_QUEUED_MESSAGES 32
|
||||
#define WS_MAX_QUEUED_MESSAGES 16
|
||||
#else
|
||||
#include <ESPAsyncTCP.h>
|
||||
#define WS_MAX_QUEUED_MESSAGES 8
|
||||
@ -33,6 +33,10 @@
|
||||
|
||||
#include "AsyncWebSynchronization.h"
|
||||
|
||||
#include <list>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <Hash.h>
|
||||
#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
|
||||
@ -40,12 +44,14 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ESP32) || defined(LIBRETUYA)
|
||||
#ifdef ESP32
|
||||
#define DEFAULT_MAX_WS_CLIENTS 8
|
||||
#else
|
||||
#define DEFAULT_MAX_WS_CLIENTS 4
|
||||
#endif
|
||||
|
||||
using AsyncWebSocketMessageBuffer = std::shared_ptr<std::vector<uint8_t>>;
|
||||
|
||||
class AsyncWebSocket;
|
||||
class AsyncWebSocketResponse;
|
||||
class AsyncWebSocketClient;
|
||||
@ -80,78 +86,26 @@ typedef enum { WS_CONTINUATION, WS_TEXT, WS_BINARY, WS_DISCONNECT = 0x08, WS_PIN
|
||||
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;
|
||||
|
||||
class AsyncWebSocketMessageBuffer {
|
||||
private:
|
||||
uint8_t * _data;
|
||||
size_t _len;
|
||||
bool _lock;
|
||||
uint32_t _count;
|
||||
|
||||
public:
|
||||
AsyncWebSocketMessageBuffer();
|
||||
AsyncWebSocketMessageBuffer(size_t size);
|
||||
AsyncWebSocketMessageBuffer(uint8_t * data, size_t size);
|
||||
AsyncWebSocketMessageBuffer(const 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);
|
||||
void lock() { _lock = true; }
|
||||
void unlock() { _lock = false; }
|
||||
uint8_t * get() { return _data; }
|
||||
size_t length() { return _len; }
|
||||
uint32_t count() { return _count; }
|
||||
bool canDelete() { return (!_count && !_lock); }
|
||||
class AsyncWebSocketMessage
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<std::vector<uint8_t>> _WSbuffer;
|
||||
uint8_t _opcode{WS_TEXT};
|
||||
bool _mask{false};
|
||||
AwsMessageStatus _status{WS_MSG_ERROR};
|
||||
size_t _sent{};
|
||||
size_t _ack{};
|
||||
size_t _acked{};
|
||||
|
||||
friend AsyncWebSocket;
|
||||
|
||||
};
|
||||
|
||||
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:
|
||||
size_t _len;
|
||||
size_t _sent;
|
||||
size_t _ack;
|
||||
size_t _acked;
|
||||
uint8_t * _data;
|
||||
public:
|
||||
AsyncWebSocketBasicMessage(const char * data, size_t len, uint8_t opcode=WS_TEXT, bool mask=false);
|
||||
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 ;
|
||||
};
|
||||
AsyncWebSocketMessage(std::shared_ptr<std::vector<uint8_t>> buffer, uint8_t opcode=WS_TEXT, bool mask=false);
|
||||
|
||||
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 ;
|
||||
bool finished() const { return _status != WS_MSG_SENDING; }
|
||||
bool betweenFrames() const { return _acked == _ack; }
|
||||
|
||||
void ack(size_t len, uint32_t time);
|
||||
size_t send(AsyncClient *client);
|
||||
};
|
||||
|
||||
class AsyncWebSocketClient {
|
||||
@ -161,8 +115,10 @@ class AsyncWebSocketClient {
|
||||
uint32_t _clientId;
|
||||
AwsClientStatus _status;
|
||||
|
||||
LinkedList<AsyncWebSocketControl *> _controlQueue;
|
||||
LinkedList<AsyncWebSocketMessage *> _messageQueue;
|
||||
AsyncWebLock _lock;
|
||||
|
||||
std::deque<AsyncWebSocketControl> _controlQueue;
|
||||
std::deque<AsyncWebSocketMessage> _messageQueue;
|
||||
|
||||
uint8_t _pstate;
|
||||
AwsFrameInfo _pinfo;
|
||||
@ -170,9 +126,10 @@ class AsyncWebSocketClient {
|
||||
uint32_t _lastMessageTime;
|
||||
uint32_t _keepAlivePeriod;
|
||||
|
||||
void _queueMessage(AsyncWebSocketMessage *dataMessage);
|
||||
void _queueControl(AsyncWebSocketControl *controlMessage);
|
||||
void _queueControl(uint8_t opcode, const uint8_t *data=NULL, size_t len=0, bool mask=false);
|
||||
void _queueMessage(std::shared_ptr<std::vector<uint8_t>> buffer, uint8_t opcode=WS_TEXT, bool mask=false);
|
||||
void _runQueue();
|
||||
void _clearQueue();
|
||||
|
||||
public:
|
||||
void *_tempObject;
|
||||
@ -181,18 +138,22 @@ class AsyncWebSocketClient {
|
||||
~AsyncWebSocketClient();
|
||||
|
||||
//client id increments for the given server
|
||||
uint32_t id(){ return _clientId; }
|
||||
AwsClientStatus status(){ return _status; }
|
||||
AsyncClient* client(){ return _client; }
|
||||
uint32_t id() const { return _clientId; }
|
||||
AwsClientStatus status() const { return _status; }
|
||||
AsyncClient* client() { return _client; }
|
||||
const AsyncClient* client() const { return _client; }
|
||||
AsyncWebSocket *server(){ return _server; }
|
||||
const AsyncWebSocket *server() const { return _server; }
|
||||
AwsFrameInfo const &pinfo() const { return _pinfo; }
|
||||
|
||||
IPAddress remoteIP();
|
||||
uint16_t remotePort();
|
||||
IPAddress remoteIP() const;
|
||||
uint16_t remotePort() const;
|
||||
|
||||
bool shouldBeDeleted() const { return !_client; }
|
||||
|
||||
//control frames
|
||||
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)
|
||||
void keepAlivePeriod(uint16_t seconds){
|
||||
@ -203,30 +164,49 @@ class AsyncWebSocketClient {
|
||||
}
|
||||
|
||||
//data packets
|
||||
void message(AsyncWebSocketMessage *message){ _queueMessage(message); }
|
||||
bool queueIsFull();
|
||||
/**
|
||||
* @brief allocate memory buffer owned by shared-pointer and copy provided data
|
||||
* used to keep the data untill websocket send is complete for single/multiple clients
|
||||
*
|
||||
* @param message
|
||||
* @param len
|
||||
* @return AsyncWebSocketMessageBuffer
|
||||
*/
|
||||
AsyncWebSocketMessageBuffer makeBuffer(const uint8_t *message, size_t len);
|
||||
|
||||
/**
|
||||
* @brief allocate empty memory buffer owned by shared-pointer
|
||||
* used to keep the data untill websocket send is complete for single/multiple clients
|
||||
*
|
||||
* @param len
|
||||
* @return AsyncWebSocketMessageBuffer
|
||||
*/
|
||||
inline AsyncWebSocketMessageBuffer makeBuffer(size_t len){ return std::make_shared<std::vector<uint8_t>>(len); };
|
||||
|
||||
void message(AsyncWebSocketMessageBuffer buffer, uint8_t opcode=WS_TEXT, bool mask=false) { _queueMessage(buffer, opcode, mask); }
|
||||
bool queueIsFull() const;
|
||||
size_t queueLen() const;
|
||||
|
||||
size_t printf(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
#ifndef ESP32
|
||||
size_t printf_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
#endif
|
||||
void text(const char * message, size_t len);
|
||||
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 __FlashStringHelper *data);
|
||||
void text(AsyncWebSocketMessageBuffer *buffer);
|
||||
|
||||
void binary(const char * message, size_t len);
|
||||
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 __FlashStringHelper *data, size_t len);
|
||||
void binary(AsyncWebSocketMessageBuffer *buffer);
|
||||
inline void text(AsyncWebSocketMessageBuffer buffer){ _queueMessage(buffer); };
|
||||
inline void text(const uint8_t *message, size_t len){ text(makeBuffer(message, len)); };
|
||||
inline void text(const char *message, size_t len){ text((const uint8_t *)message, len); };
|
||||
inline void text(const char *message){ text(message, strlen(message)); };
|
||||
inline void text(const String &message){ text(message.c_str(), message.length()); };
|
||||
void text(const __FlashStringHelper *message);
|
||||
|
||||
bool canSend() { return _messageQueue.length() < WS_MAX_QUEUED_MESSAGES; }
|
||||
inline void binary(AsyncWebSocketMessageBuffer buffer){ _queueMessage(buffer, WS_BINARY); };
|
||||
inline void binary(const uint8_t *message, size_t len){ binary(makeBuffer(message, len)); };
|
||||
inline void binary(const char * message, size_t len){ binary((const uint8_t *)message, len); };
|
||||
inline void binary(const char * message){ binary(message, strlen(message)); };
|
||||
inline void binary(const String &message){ binary(message.c_str(), message.length()); };
|
||||
void binary(const __FlashStringHelper *message, size_t len);
|
||||
|
||||
bool canSend() const;
|
||||
|
||||
//system callbacks (do not call)
|
||||
void _onAck(size_t len, uint32_t time);
|
||||
@ -237,17 +217,17 @@ class AsyncWebSocketClient {
|
||||
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;
|
||||
|
||||
//WebServer Handler implementation that plays the role of a socket server
|
||||
class AsyncWebSocket: public AsyncWebHandler {
|
||||
public:
|
||||
typedef LinkedList<AsyncWebSocketClient *> AsyncWebSocketClientLinkedList;
|
||||
private:
|
||||
String _url;
|
||||
AsyncWebSocketClientLinkedList _clients;
|
||||
std::list<AsyncWebSocketClient> _clients;
|
||||
uint32_t _cNextId;
|
||||
AwsEventHandler _eventHandler;
|
||||
AwsHandshakeHandler _handshakeHandler;
|
||||
bool _enabled;
|
||||
AsyncWebLock _lock;
|
||||
|
||||
@ -268,41 +248,56 @@ class AsyncWebSocket: public AsyncWebHandler {
|
||||
void closeAll(uint16_t code=0, const char * message=NULL);
|
||||
void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS);
|
||||
|
||||
void ping(uint32_t id, uint8_t *data=NULL, size_t len=0);
|
||||
void pingAll(uint8_t *data=NULL, size_t len=0); // done
|
||||
void ping(uint32_t id, const uint8_t *data=NULL, size_t len=0);
|
||||
void pingAll(const uint8_t *data=NULL, size_t len=0); // done
|
||||
|
||||
void text(uint32_t id, const char * message, size_t len);
|
||||
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);
|
||||
//data packets
|
||||
/**
|
||||
* @brief allocate memory buffer owned by shared-pointer and copy provided data
|
||||
* used to keep the data untill websocket send is complete for single/multiple clients
|
||||
*
|
||||
* @param message
|
||||
* @param len
|
||||
* @return AsyncWebSocketMessageBuffer
|
||||
*/
|
||||
AsyncWebSocketMessageBuffer makeBuffer(const uint8_t *message, size_t len);
|
||||
|
||||
/**
|
||||
* @brief allocate empty memory buffer owned by shared-pointer
|
||||
* used to keep the data untill websocket send is complete for single/multiple clients
|
||||
*
|
||||
* @param len
|
||||
* @return AsyncWebSocketMessageBuffer
|
||||
*/
|
||||
inline AsyncWebSocketMessageBuffer makeBuffer(size_t len){ return std::make_shared<std::vector<uint8_t>>(len); };
|
||||
|
||||
void text(uint32_t id, AsyncWebSocketMessageBuffer message);
|
||||
void text(uint32_t id, const uint8_t * message, size_t len);
|
||||
inline void text(uint32_t id, const char *message, size_t len){ text(id, (const uint8_t *)message, len); };
|
||||
inline void text(uint32_t id, const char *message){ text(id, message, strlen(message)); };
|
||||
inline void text(uint32_t id, const String &message){ text(id, message.c_str(), message.length()); };
|
||||
void text(uint32_t id, const __FlashStringHelper *message);
|
||||
|
||||
void textAll(AsyncWebSocketMessageBuffer buffer);
|
||||
void textAll(const uint8_t *message, size_t len);
|
||||
void textAll(const char * message, size_t len);
|
||||
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 __FlashStringHelper *message); // need to convert
|
||||
void textAll(AsyncWebSocketMessageBuffer * buffer);
|
||||
|
||||
void binary(uint32_t id, const char * message, size_t len);
|
||||
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, AsyncWebSocketMessageBuffer message);
|
||||
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);
|
||||
void binary(uint32_t id, const String &message);
|
||||
void binary(uint32_t id, const __FlashStringHelper *message, size_t len);
|
||||
|
||||
void binaryAll(const char * message, size_t len);
|
||||
void binaryAll(const char * message);
|
||||
void binaryAll(uint8_t * message, size_t len);
|
||||
void binaryAll(char * message);
|
||||
void binaryAll(AsyncWebSocketMessageBuffer buffer);
|
||||
void binaryAll(const uint8_t *message, size_t len);
|
||||
void binaryAll(const char *message, size_t len);
|
||||
void binaryAll(const char *message);
|
||||
void binaryAll(const String &message);
|
||||
void binaryAll(const __FlashStringHelper *message, size_t len);
|
||||
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 printfAll(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
@ -316,22 +311,19 @@ class AsyncWebSocket: public AsyncWebHandler {
|
||||
_eventHandler = handler;
|
||||
}
|
||||
|
||||
// Handshake Handler
|
||||
void handleHandshake(AwsHandshakeHandler handler){
|
||||
_handshakeHandler = handler;
|
||||
}
|
||||
|
||||
//system callbacks (do not call)
|
||||
uint32_t _getNextId(){ return _cNextId++; }
|
||||
void _addClient(AsyncWebSocketClient * client);
|
||||
void _handleDisconnect(AsyncWebSocketClient * client);
|
||||
AsyncWebSocketClient *_newClient(AsyncWebServerRequest *request);
|
||||
void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len);
|
||||
virtual bool canHandle(AsyncWebServerRequest *request) override final;
|
||||
virtual void handleRequest(AsyncWebServerRequest *request) override final;
|
||||
|
||||
|
||||
// messagebuffer functions/objects.
|
||||
AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0);
|
||||
AsyncWebSocketMessageBuffer * makeBuffer(uint8_t * data, size_t size);
|
||||
LinkedList<AsyncWebSocketMessageBuffer *> _buffers;
|
||||
void _cleanBuffers();
|
||||
|
||||
AsyncWebSocketClientLinkedList getClients() const;
|
||||
const std::list<AsyncWebSocketClient> &getClients() const { return _clients; }
|
||||
};
|
||||
|
||||
//WebServer response to authenticate the socket and detach the tcp client from the web server request
|
||||
|
@ -5,40 +5,76 @@
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#if defined(ESP32) || (defined(LIBRETUYA) && LT_HAS_FREERTOS)
|
||||
#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 void *_lockedBy;
|
||||
SemaphoreHandle_t _lock;
|
||||
mutable TaskHandle_t _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;
|
||||
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);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void unlock() const {
|
||||
_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
|
||||
@ -61,6 +97,10 @@ public:
|
||||
void unlock() const {
|
||||
}
|
||||
};
|
||||
|
||||
// Same for AsyncPlainLock, for ESP8266 this is just the unimplemented version above.
|
||||
using AsyncPlainLock = AsyncWebLock;
|
||||
|
||||
#endif
|
||||
|
||||
class AsyncWebLockGuard
|
||||
@ -82,6 +122,13 @@ public:
|
||||
_lock->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
if (_lock) {
|
||||
_lock->unlock();
|
||||
_lock = NULL;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ASYNCWEBSYNCHRONIZATION_H_
|
||||
#endif // ASYNCWEBSYNCHRONIZATION_H_
|
||||
|
@ -24,11 +24,13 @@
|
||||
#include "Arduino.h"
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include "FS.h"
|
||||
|
||||
#include "StringArray.h"
|
||||
|
||||
#if defined(ESP32) || defined(LIBRETUYA)
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#include <AsyncTCP.h>
|
||||
#elif defined(ESP8266)
|
||||
@ -70,6 +72,19 @@ typedef enum {
|
||||
} WebRequestMethod;
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_FS_FILE_OPEN_MODE
|
||||
namespace fs {
|
||||
class FileOpenMode {
|
||||
public:
|
||||
static const char *read;
|
||||
static const char *write;
|
||||
static const char *append;
|
||||
};
|
||||
};
|
||||
#else
|
||||
#include "FileOpenMode.h"
|
||||
#endif
|
||||
|
||||
//if this value is returned when asked for data, packet will not be sent and you will be asked for data again
|
||||
#define RESPONSE_TRY_AGAIN 0xFFFFFFFF
|
||||
|
||||
@ -108,6 +123,9 @@ class AsyncWebHeader {
|
||||
String _value;
|
||||
|
||||
public:
|
||||
AsyncWebHeader() = default;
|
||||
AsyncWebHeader(const AsyncWebHeader &) = default;
|
||||
|
||||
AsyncWebHeader(const String& name, const String& value): _name(name), _value(value){}
|
||||
AsyncWebHeader(const String& data): _name(), _value(){
|
||||
if(!data) return;
|
||||
@ -116,10 +134,12 @@ class AsyncWebHeader {
|
||||
_name = data.substring(0, index);
|
||||
_value = data.substring(index + 2);
|
||||
}
|
||||
~AsyncWebHeader(){}
|
||||
|
||||
AsyncWebHeader &operator=(const AsyncWebHeader &) = default;
|
||||
|
||||
const String& name() const { return _name; }
|
||||
const String& value() const { return _value; }
|
||||
String toString() const { return String(_name+": "+_value+"\r\n"); }
|
||||
String toString() const { return _name + F(": ") + _value + F("\r\n"); }
|
||||
};
|
||||
|
||||
/*
|
||||
@ -141,7 +161,7 @@ class AsyncWebServerRequest {
|
||||
AsyncWebServer* _server;
|
||||
AsyncWebHandler* _handler;
|
||||
AsyncWebServerResponse* _response;
|
||||
StringArray _interestingHeaders;
|
||||
std::vector<String> _interestingHeaders;
|
||||
ArDisconnectHandler _onDisconnectfn;
|
||||
|
||||
String _temp;
|
||||
@ -163,9 +183,9 @@ class AsyncWebServerRequest {
|
||||
size_t _contentLength;
|
||||
size_t _parsedLength;
|
||||
|
||||
LinkedList<AsyncWebHeader *> _headers;
|
||||
std::list<AsyncWebHeader> _headers;
|
||||
LinkedList<AsyncWebParameter *> _params;
|
||||
LinkedList<String *> _pathParams;
|
||||
std::vector<String> _pathParams;
|
||||
|
||||
uint8_t _multiParseState;
|
||||
uint8_t _boundaryPosition;
|
||||
@ -215,8 +235,8 @@ class AsyncWebServerRequest {
|
||||
const String& contentType() const { return _contentType; }
|
||||
size_t contentLength() const { return _contentLength; }
|
||||
bool multipart() const { return _isMultipart; }
|
||||
const char * methodToString() const;
|
||||
const char * requestedConnTypeToString() const;
|
||||
const __FlashStringHelper *methodToString() const;
|
||||
const __FlashStringHelper *requestedConnTypeToString() const;
|
||||
RequestedConnectionType requestedConnType() const { return _reqconntype; }
|
||||
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
|
||||
void onDisconnect (ArDisconnectHandler fn);
|
||||
@ -257,21 +277,24 @@ class AsyncWebServerRequest {
|
||||
bool hasHeader(const String& name) const; // check if header exists
|
||||
bool hasHeader(const __FlashStringHelper * data) const; // check if header exists
|
||||
|
||||
AsyncWebHeader* getHeader(const String& name) const;
|
||||
AsyncWebHeader* getHeader(const __FlashStringHelper * data) const;
|
||||
AsyncWebHeader* getHeader(size_t num) const;
|
||||
AsyncWebHeader* getHeader(const String& name);
|
||||
const AsyncWebHeader* getHeader(const String& name) 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
|
||||
bool hasParam(const String& name, bool post=false, bool file=false) const;
|
||||
bool hasParam(const __FlashStringHelper * data, bool post=false, bool file=false) const;
|
||||
|
||||
AsyncWebParameter* getParam(const String& name, bool post=false, bool file=false) const;
|
||||
AsyncWebParameter* getParam(const __FlashStringHelper * data, bool post, bool file) const;
|
||||
AsyncWebParameter* getParam(const __FlashStringHelper * data, bool post, bool file) const;
|
||||
AsyncWebParameter* getParam(size_t num) const;
|
||||
|
||||
size_t args() const { return params(); } // get arguments count
|
||||
const String& arg(const String& name) const; // get request argument value by name
|
||||
const String& arg(const __FlashStringHelper * data) const; // get request argument value by F(name)
|
||||
const String& arg(const __FlashStringHelper * data) const; // get request argument value by F(name)
|
||||
const String& arg(size_t i) const; // get request argument value by number
|
||||
const String& argName(size_t i) const; // get request argument name by number
|
||||
bool hasArg(const char* name) const; // check if argument exists
|
||||
@ -280,7 +303,7 @@ class AsyncWebServerRequest {
|
||||
const String& ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const;
|
||||
|
||||
const String& header(const char* name) const;// get request header value by name
|
||||
const String& header(const __FlashStringHelper * data) const;// get request header value by F(name)
|
||||
const String& header(const __FlashStringHelper * data) const;// get request header value by F(name)
|
||||
const String& header(size_t i) const; // get request header value by number
|
||||
const String& headerName(size_t i) const; // get request header name by number
|
||||
String urlDecode(const String& text) const;
|
||||
@ -358,7 +381,7 @@ typedef enum {
|
||||
class AsyncWebServerResponse {
|
||||
protected:
|
||||
int _code;
|
||||
LinkedList<AsyncWebHeader *> _headers;
|
||||
std::list<AsyncWebHeader> _headers;
|
||||
String _contentType;
|
||||
size_t _contentLength;
|
||||
bool _sendContentLength;
|
||||
@ -369,6 +392,8 @@ class AsyncWebServerResponse {
|
||||
size_t _writtenLength;
|
||||
WebResponseState _state;
|
||||
const char* _responseCodeToString(int code);
|
||||
public:
|
||||
static const __FlashStringHelper *responseCodeToString(int code);
|
||||
|
||||
public:
|
||||
AsyncWebServerResponse();
|
||||
@ -419,7 +444,7 @@ class AsyncWebServer {
|
||||
|
||||
AsyncWebHandler& addHandler(AsyncWebHandler* handler);
|
||||
bool removeHandler(AsyncWebHandler* handler);
|
||||
|
||||
|
||||
AsyncCallbackWebHandler& on(const char* uri, ArRequestHandlerFunction onRequest);
|
||||
AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest);
|
||||
AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload);
|
||||
@ -431,32 +456,32 @@ class AsyncWebServer {
|
||||
void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads
|
||||
void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request)
|
||||
|
||||
void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody
|
||||
|
||||
void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody
|
||||
|
||||
void _handleDisconnect(AsyncWebServerRequest *request);
|
||||
void _attachHandler(AsyncWebServerRequest *request);
|
||||
void _rewriteRequest(AsyncWebServerRequest *request);
|
||||
};
|
||||
|
||||
class DefaultHeaders {
|
||||
using headers_t = LinkedList<AsyncWebHeader *>;
|
||||
using headers_t = std::list<AsyncWebHeader>;
|
||||
headers_t _headers;
|
||||
|
||||
DefaultHeaders()
|
||||
:_headers(headers_t([](AsyncWebHeader *h){ delete h; }))
|
||||
{}
|
||||
|
||||
public:
|
||||
using ConstIterator = headers_t::ConstIterator;
|
||||
DefaultHeaders() = default;
|
||||
|
||||
using ConstIterator = headers_t::const_iterator;
|
||||
|
||||
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 end() const { return _headers.end(); }
|
||||
|
||||
DefaultHeaders(DefaultHeaders const &) = delete;
|
||||
DefaultHeaders &operator=(DefaultHeaders const &) = delete;
|
||||
|
||||
static DefaultHeaders &Instance() {
|
||||
static DefaultHeaders instance;
|
||||
return instance;
|
||||
|
342
src/SPIFFSEditor.cpp
Normal file
342
src/SPIFFSEditor.cpp
Normal file
@ -0,0 +1,342 @@
|
||||
#include "SPIFFSEditor.h"
|
||||
#include <FS.h>
|
||||
|
||||
#define EDFS
|
||||
|
||||
#ifndef EDFS
|
||||
#include "edit.htm.gz.h"
|
||||
#endif
|
||||
|
||||
#ifdef ESP32
|
||||
#define fullName(x) name(x)
|
||||
#endif
|
||||
|
||||
#define SPIFFS_MAXLENGTH_FILEPATH 32
|
||||
static const char excludeListFile[] PROGMEM = "/.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, String(FPSTR(excludeListFile)).c_str());
|
||||
}
|
||||
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(F("/edit"))){
|
||||
if(request->method() == HTTP_GET){
|
||||
if(request->hasParam(F("list")))
|
||||
return true;
|
||||
if(request->hasParam(F("edit"))){
|
||||
request->_tempFile = _fs.open(request->arg(F("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(F("download")), "r");
|
||||
if(!request->_tempFile){
|
||||
return false;
|
||||
}
|
||||
#ifdef ESP32
|
||||
if(request->_tempFile.isDirectory()){
|
||||
request->_tempFile.close();
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
request->addInterestingHeader(F("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(F("list"))){
|
||||
String path = request->getParam(F("list"))->value();
|
||||
#ifdef ESP32
|
||||
File dir = _fs.open(path);
|
||||
#else
|
||||
fs::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
|
||||
String fname = entry.fullName();
|
||||
if (fname.charAt(0) != '/') fname = "/" + fname;
|
||||
|
||||
if (isExcluded(_fs, fname.c_str())) {
|
||||
#ifdef ESP32
|
||||
entry = dir.openNextFile();
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
if (output != "[") output += ',';
|
||||
output += F("{\"type\":\"");
|
||||
output += F("file");
|
||||
output += F("\",\"name\":\"");
|
||||
output += String(fname);
|
||||
output += F("\",\"size\":");
|
||||
output += String(entry.size());
|
||||
output += "}";
|
||||
#ifdef ESP32
|
||||
entry = dir.openNextFile();
|
||||
#else
|
||||
entry.close();
|
||||
#endif
|
||||
}
|
||||
#ifdef ESP32
|
||||
dir.close();
|
||||
#endif
|
||||
output += "]";
|
||||
request->send(200, F("application/json"), output);
|
||||
output = String();
|
||||
}
|
||||
else if(request->hasParam(F("edit")) || request->hasParam(F("download"))){
|
||||
request->send(request->_tempFile, request->_tempFile.fullName(), String(), request->hasParam(F("download")));
|
||||
}
|
||||
else {
|
||||
const char * buildTime = __DATE__ " " __TIME__ " GMT";
|
||||
if (request->header(F("If-Modified-Since")).equals(buildTime)) {
|
||||
request->send(304);
|
||||
} else {
|
||||
#ifdef EDFS
|
||||
AsyncWebServerResponse *response = request->beginResponse(_fs, F("/edit_gz"), F("text/html"), false);
|
||||
#else
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html"), edit_htm_gz, edit_htm_gz_len);
|
||||
#endif
|
||||
response->addHeader(F("Content-Encoding"), F("gzip"));
|
||||
response->addHeader(F("Last-Modified"), buildTime);
|
||||
request->send(response);
|
||||
}
|
||||
}
|
||||
} else if(request->method() == HTTP_DELETE){
|
||||
if(request->hasParam(F("path"), true)){
|
||||
if(!(_fs.remove(request->getParam(F("path"), true)->value()))){
|
||||
#ifdef ESP32
|
||||
_fs.rmdir(request->getParam(F("path"), true)->value()); // try rmdir for littlefs
|
||||
#endif
|
||||
}
|
||||
|
||||
request->send(200, "", String(F("DELETE: "))+request->getParam(F("path"), true)->value());
|
||||
} else
|
||||
request->send(404);
|
||||
} else if(request->method() == HTTP_POST){
|
||||
if(request->hasParam(F("data"), true, true) && _fs.exists(request->getParam(F("data"), true, true)->value()))
|
||||
request->send(200, "", String(F("UPLOADED: "))+request->getParam(F("data"), true, true)->value());
|
||||
|
||||
else if(request->hasParam(F("rawname"), true) && request->hasParam(F("raw0"), true)){
|
||||
String rawnam = request->getParam(F("rawname"), true)->value();
|
||||
|
||||
if (_fs.exists(rawnam)) _fs.remove(rawnam); // delete it to allow a mode
|
||||
|
||||
int k = 0;
|
||||
uint16_t i = 0;
|
||||
fs::File f = _fs.open(rawnam, "a");
|
||||
|
||||
while (request->hasParam(String(F("raw")) + String(k), true)) { //raw0 .. raw1
|
||||
if(f){
|
||||
i += f.print(request->getParam(String(F("raw")) + String(k), true)->value());
|
||||
}
|
||||
k++;
|
||||
}
|
||||
f.close();
|
||||
request->send(200, "", String(F("IPADWRITE: ")) + rawnam + ":" + String(i));
|
||||
|
||||
} else {
|
||||
request->send(500);
|
||||
}
|
||||
|
||||
} else if(request->method() == HTTP_PUT){
|
||||
if(request->hasParam(F("path"), true)){
|
||||
String filename = request->getParam(F("path"), true)->value();
|
||||
if(_fs.exists(filename)){
|
||||
request->send(200);
|
||||
} else {
|
||||
/*******************************************************/
|
||||
#ifdef ESP32
|
||||
if (strchr(filename.c_str(), '/')) {
|
||||
// For file creation, silently make subdirs as needed. If any fail,
|
||||
// it will be caught by the real file open later on
|
||||
char *pathStr = strdup(filename.c_str());
|
||||
if (pathStr) {
|
||||
// Make dirs up to the final fnamepart
|
||||
char *ptr = strchr(pathStr, '/');
|
||||
while (ptr) {
|
||||
*ptr = 0;
|
||||
_fs.mkdir(pathStr);
|
||||
*ptr = '/';
|
||||
ptr = strchr(ptr+1, '/');
|
||||
}
|
||||
}
|
||||
free(pathStr);
|
||||
}
|
||||
#endif
|
||||
/*******************************************************/
|
||||
fs::File f = _fs.open(filename, "w");
|
||||
if(f){
|
||||
f.write((uint8_t)0x00);
|
||||
f.close();
|
||||
request->send(200, "", String(F("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();
|
||||
}
|
||||
}
|
||||
}
|
25
src/SPIFFSEditor.h
Normal file
25
src/SPIFFSEditor.h
Normal file
@ -0,0 +1,25 @@
|
||||
#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
|
||||
//SPIFFSEditor(const String& username=String(), const String& password=String(), const fs::FS& fs=SPIFFS);
|
||||
SPIFFSEditor(const String& username, const String& password, const fs::FS& fs); // do not show warning that SPIFFS has been deprecated
|
||||
#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
|
@ -47,19 +47,10 @@ class LinkedList {
|
||||
|
||||
class Iterator {
|
||||
ItemType* _node;
|
||||
ItemType* _nextNode = nullptr;
|
||||
public:
|
||||
Iterator(ItemType* current = nullptr) : _node(current) {
|
||||
if (_node != nullptr) {
|
||||
_nextNode = current->next;
|
||||
}
|
||||
}
|
||||
Iterator(ItemType* current = nullptr) : _node(current) {}
|
||||
Iterator(const Iterator& i) : _node(i._node) {}
|
||||
Iterator& operator ++() {
|
||||
_node = _nextNode;
|
||||
_nextNode = _node != nullptr ? _node->next : nullptr;
|
||||
return *this;
|
||||
}
|
||||
Iterator& operator ++() { _node = _node->next; return *this; }
|
||||
bool operator != (const Iterator& i) const { return _node != i._node; }
|
||||
const T& operator * () const { return _node->value(); }
|
||||
const T* operator -> () const { return &_node->value(); }
|
||||
@ -180,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_ */
|
||||
|
@ -36,6 +36,12 @@ bool checkBasicAuthentication(const char * hash, const char * username, const ch
|
||||
size_t toencodeLen = strlen(username)+strlen(password)+1;
|
||||
size_t encodedLen = base64_encode_expected_len(toencodeLen);
|
||||
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;
|
||||
|
||||
char *toencode = new char[toencodeLen+1];
|
||||
@ -47,7 +53,7 @@ bool checkBasicAuthentication(const char * hash, const char * username, const ch
|
||||
delete[] toencode;
|
||||
return false;
|
||||
}
|
||||
sprintf(toencode, "%s:%s", username, password);
|
||||
sprintf_P(toencode, PSTR("%s:%s"), username, password);
|
||||
if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0){
|
||||
delete[] toencode;
|
||||
delete[] encoded;
|
||||
@ -80,7 +86,7 @@ static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or mo
|
||||
MD5Final(_buf, &_ctx);
|
||||
#endif
|
||||
for(i = 0; i < 16; i++) {
|
||||
sprintf(output + (i * 2), "%02x", _buf[i]);
|
||||
sprintf_P(output + (i * 2), PSTR("%02x"), _buf[i]);
|
||||
}
|
||||
free(_buf);
|
||||
return true;
|
||||
@ -94,7 +100,7 @@ static String genRandomMD5(){
|
||||
#endif
|
||||
char * out = (char*)malloc(33);
|
||||
if(out == NULL || !getMD5((uint8_t*)(&r), 4, out))
|
||||
return "";
|
||||
return emptyString;
|
||||
String res = String(out);
|
||||
free(out);
|
||||
return res;
|
||||
@ -103,7 +109,7 @@ static String genRandomMD5(){
|
||||
static String stringMD5(const String& in){
|
||||
char * out = (char*)malloc(33);
|
||||
if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
|
||||
return "";
|
||||
return emptyString;
|
||||
String res = String(out);
|
||||
free(out);
|
||||
return res;
|
||||
@ -111,44 +117,44 @@ static String stringMD5(const String& in){
|
||||
|
||||
String generateDigestHash(const char * username, const char * password, const char * realm){
|
||||
if(username == NULL || password == NULL || realm == NULL){
|
||||
return "";
|
||||
return emptyString;
|
||||
}
|
||||
char * out = (char*)malloc(33);
|
||||
String res = String(username);
|
||||
res.concat(":");
|
||||
res += ':';
|
||||
res.concat(realm);
|
||||
res.concat(":");
|
||||
res += ':';
|
||||
String in = res;
|
||||
in.concat(password);
|
||||
if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
|
||||
return "";
|
||||
return emptyString;
|
||||
res.concat(out);
|
||||
free(out);
|
||||
return res;
|
||||
}
|
||||
|
||||
String requestDigestAuthentication(const char * realm){
|
||||
String header = "realm=\"";
|
||||
String header = F("realm=\"");
|
||||
if(realm == NULL)
|
||||
header.concat("asyncesp");
|
||||
header.concat(F("asyncesp"));
|
||||
else
|
||||
header.concat(realm);
|
||||
header.concat( "\", qop=\"auth\", nonce=\"");
|
||||
header.concat(F("\", qop=\"auth\", nonce=\""));
|
||||
header.concat(genRandomMD5());
|
||||
header.concat("\", opaque=\"");
|
||||
header.concat(F("\", opaque=\""));
|
||||
header.concat(genRandomMD5());
|
||||
header.concat("\"");
|
||||
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){
|
||||
//os_printf("AUTH FAIL: missing requred fields\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
String myHeader = String(header);
|
||||
int nextBreak = myHeader.indexOf(",");
|
||||
int nextBreak = myHeader.indexOf(',');
|
||||
if(nextBreak < 0){
|
||||
//os_printf("AUTH FAIL: no variables\n");
|
||||
return false;
|
||||
@ -163,67 +169,67 @@ bool checkDigestAuthentication(const char * header, const char * method, const c
|
||||
String myNc = String();
|
||||
String myCnonce = String();
|
||||
|
||||
myHeader += ", ";
|
||||
myHeader += F(", ");
|
||||
do {
|
||||
String avLine = myHeader.substring(0, nextBreak);
|
||||
avLine.trim();
|
||||
myHeader = myHeader.substring(nextBreak+1);
|
||||
nextBreak = myHeader.indexOf(",");
|
||||
nextBreak = myHeader.indexOf(',');
|
||||
|
||||
int eqSign = avLine.indexOf("=");
|
||||
int eqSign = avLine.indexOf('=');
|
||||
if(eqSign < 0){
|
||||
//os_printf("AUTH FAIL: no = sign\n");
|
||||
return false;
|
||||
}
|
||||
String varName = avLine.substring(0, eqSign);
|
||||
avLine = avLine.substring(eqSign + 1);
|
||||
if(avLine.startsWith("\"")){
|
||||
if(avLine.startsWith(String('"'))){
|
||||
avLine = avLine.substring(1, avLine.length() - 1);
|
||||
}
|
||||
|
||||
if(varName.equals("username")){
|
||||
if(varName.equals(F("username"))){
|
||||
if(!avLine.equals(username)){
|
||||
//os_printf("AUTH FAIL: username\n");
|
||||
return false;
|
||||
}
|
||||
myUsername = avLine;
|
||||
} else if(varName.equals("realm")){
|
||||
} else if(varName.equals(F("realm"))){
|
||||
if(realm != NULL && !avLine.equals(realm)){
|
||||
//os_printf("AUTH FAIL: realm\n");
|
||||
return false;
|
||||
}
|
||||
myRealm = avLine;
|
||||
} else if(varName.equals("nonce")){
|
||||
} else if(varName.equals(F("nonce"))){
|
||||
if(nonce != NULL && !avLine.equals(nonce)){
|
||||
//os_printf("AUTH FAIL: nonce\n");
|
||||
return false;
|
||||
}
|
||||
myNonce = avLine;
|
||||
} else if(varName.equals("opaque")){
|
||||
} else if(varName.equals(F("opaque"))){
|
||||
if(opaque != NULL && !avLine.equals(opaque)){
|
||||
//os_printf("AUTH FAIL: opaque\n");
|
||||
return false;
|
||||
}
|
||||
} else if(varName.equals("uri")){
|
||||
} else if(varName.equals(F("uri"))){
|
||||
if(uri != NULL && !avLine.equals(uri)){
|
||||
//os_printf("AUTH FAIL: uri\n");
|
||||
return false;
|
||||
}
|
||||
myUri = avLine;
|
||||
} else if(varName.equals("response")){
|
||||
} else if(varName.equals(F("response"))){
|
||||
myResponse = avLine;
|
||||
} else if(varName.equals("qop")){
|
||||
} else if(varName.equals(F("qop"))){
|
||||
myQop = avLine;
|
||||
} else if(varName.equals("nc")){
|
||||
} else if(varName.equals(F("nc"))){
|
||||
myNc = avLine;
|
||||
} else if(varName.equals("cnonce")){
|
||||
} else if(varName.equals(F("cnonce"))){
|
||||
myCnonce = avLine;
|
||||
}
|
||||
} while(nextBreak > 0);
|
||||
|
||||
String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ":" + myRealm + ":" + String(password));
|
||||
String ha2 = String(method) + ":" + myUri;
|
||||
String response = ha1 + ":" + myNonce + ":" + myNc + ":" + myCnonce + ":" + myQop + ":" + stringMD5(ha2);
|
||||
String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ':' + myRealm + ':' + String(password));
|
||||
String ha2 = String(method) + ':' + myUri;
|
||||
String response = ha1 + ':' + myNonce + ':' + myNc + ':' + myCnonce + ':' + myQop + ':' + stringMD5(ha2);
|
||||
|
||||
if(myResponse.equals(stringMD5(response))){
|
||||
//os_printf("AUTH SUCCESS\n");
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
bool checkBasicAuthentication(const char * header, const char * username, const char * password);
|
||||
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
|
||||
String generateDigestHash(const char * username, const char * password, const char * realm);
|
||||
|
@ -22,11 +22,11 @@
|
||||
#include "WebHandlerImpl.h"
|
||||
|
||||
AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control)
|
||||
: _fs(fs), _uri(uri), _path(path), _default_file("index.htm"), _cache_control(cache_control), _last_modified(""), _callback(nullptr)
|
||||
: _fs(fs), _uri(uri), _path(path), _default_file(F("index.htm")), _cache_control(cache_control), _last_modified(), _callback(nullptr)
|
||||
{
|
||||
// Ensure leading '/'
|
||||
if (_uri.length() == 0 || _uri[0] != '/') _uri = "/" + _uri;
|
||||
if (_path.length() == 0 || _path[0] != '/') _path = "/" + _path;
|
||||
if (_uri.length() == 0 || _uri[0] != '/') _uri = String('/') + _uri;
|
||||
if (_path.length() == 0 || _path[0] != '/') _path = String('/') + _path;
|
||||
|
||||
// If path ends with '/' we assume a hint that this is a directory to improve performance.
|
||||
// However - if it does not end with '/' we, can't assume a file, path can still be a directory.
|
||||
@ -63,8 +63,12 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_m
|
||||
}
|
||||
|
||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified){
|
||||
auto formatP = PSTR("%a, %d %b %Y %H:%M:%S %Z");
|
||||
char format[strlen_P(formatP) + 1];
|
||||
strcpy_P(format, formatP);
|
||||
|
||||
char result[30];
|
||||
strftime (result,30,"%a, %d %b %Y %H:%M:%S %Z", last_modified);
|
||||
strftime(result, sizeof(result), format, last_modified);
|
||||
return setLastModified((const char *)result);
|
||||
}
|
||||
|
||||
@ -81,8 +85,8 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(){
|
||||
}
|
||||
#endif
|
||||
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){
|
||||
if(request->method() != HTTP_GET
|
||||
|| !request->url().startsWith(_uri)
|
||||
if(request->method() != HTTP_GET
|
||||
|| !request->url().startsWith(_uri)
|
||||
|| !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP)
|
||||
){
|
||||
return false;
|
||||
@ -90,10 +94,10 @@ bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){
|
||||
if (_getFile(request)) {
|
||||
// We interested in "If-Modified-Since" header to check if file was modified
|
||||
if (_last_modified.length())
|
||||
request->addInterestingHeader("If-Modified-Since");
|
||||
request->addInterestingHeader(F("If-Modified-Since"));
|
||||
|
||||
if(_cache_control.length())
|
||||
request->addInterestingHeader("If-None-Match");
|
||||
request->addInterestingHeader(F("If-None-Match"));
|
||||
|
||||
DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n");
|
||||
return true;
|
||||
@ -122,7 +126,7 @@ bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request)
|
||||
|
||||
// Try to add default file, ensure there is a trailing '/' ot the path.
|
||||
if (path.length() == 0 || path[path.length()-1] != '/')
|
||||
path += "/";
|
||||
path += String('/');
|
||||
path += _default_file;
|
||||
|
||||
return _fileExists(request, path);
|
||||
@ -139,21 +143,29 @@ bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const St
|
||||
bool fileFound = false;
|
||||
bool gzipFound = false;
|
||||
|
||||
String gzip = path + ".gz";
|
||||
String gzip = path + F(".gz");
|
||||
|
||||
if (_gzipFirst) {
|
||||
request->_tempFile = _fs.open(gzip, "r");
|
||||
gzipFound = FILE_IS_REAL(request->_tempFile);
|
||||
if (_fs.exists(gzip)) {
|
||||
request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read);
|
||||
gzipFound = FILE_IS_REAL(request->_tempFile);
|
||||
}
|
||||
if (!gzipFound){
|
||||
request->_tempFile = _fs.open(path, "r");
|
||||
if (_fs.exists(path)) {
|
||||
request->_tempFile = _fs.open(path, fs::FileOpenMode::read);
|
||||
fileFound = FILE_IS_REAL(request->_tempFile);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
request->_tempFile = _fs.open(path, "r");
|
||||
fileFound = FILE_IS_REAL(request->_tempFile);
|
||||
if (_fs.exists(path)) {
|
||||
request->_tempFile = _fs.open(path, fs::FileOpenMode::read);
|
||||
fileFound = FILE_IS_REAL(request->_tempFile);
|
||||
}
|
||||
if (!fileFound){
|
||||
request->_tempFile = _fs.open(gzip, "r");
|
||||
if (_fs.exists(gzip)) {
|
||||
request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read);
|
||||
gzipFound = FILE_IS_REAL(request->_tempFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,7 +175,7 @@ bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const St
|
||||
// Extract the file name from the path and keep it in _tempObject
|
||||
size_t pathLen = path.length();
|
||||
char * _tempPath = (char*)malloc(pathLen+1);
|
||||
snprintf(_tempPath, pathLen+1, "%s", path.c_str());
|
||||
snprintf_P(_tempPath, pathLen+1, PSTR("%s"), path.c_str());
|
||||
request->_tempObject = (void*)_tempPath;
|
||||
|
||||
// Calculate gzip statistic
|
||||
@ -190,27 +202,27 @@ void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
|
||||
String filename = String((char*)request->_tempObject);
|
||||
free(request->_tempObject);
|
||||
request->_tempObject = NULL;
|
||||
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
||||
if((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str()))
|
||||
return request->requestAuthentication();
|
||||
|
||||
if (request->_tempFile == true) {
|
||||
String etag = String(request->_tempFile.size());
|
||||
if (_last_modified.length() && _last_modified == request->header("If-Modified-Since")) {
|
||||
if (_last_modified.length() && _last_modified == request->header(F("If-Modified-Since"))) {
|
||||
request->_tempFile.close();
|
||||
request->send(304); // Not modified
|
||||
} else if (_cache_control.length() && request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag)) {
|
||||
} else if (_cache_control.length() && request->hasHeader(F("If-None-Match")) && request->header(F("If-None-Match")).equals(etag)) {
|
||||
request->_tempFile.close();
|
||||
AsyncWebServerResponse * response = new AsyncBasicResponse(304); // Not modified
|
||||
response->addHeader("Cache-Control", _cache_control);
|
||||
response->addHeader("ETag", etag);
|
||||
response->addHeader(F("Cache-Control"), _cache_control);
|
||||
response->addHeader(F("ETag"), etag);
|
||||
request->send(response);
|
||||
} else {
|
||||
AsyncWebServerResponse * response = new AsyncFileResponse(request->_tempFile, filename, String(), false, _callback);
|
||||
if (_last_modified.length())
|
||||
response->addHeader("Last-Modified", _last_modified);
|
||||
response->addHeader(F("Last-Modified"), _last_modified);
|
||||
if (_cache_control.length()){
|
||||
response->addHeader("Cache-Control", _cache_control);
|
||||
response->addHeader("ETag", etag);
|
||||
response->addHeader(F("Cache-Control"), _cache_control);
|
||||
response->addHeader(F("ETag"), etag);
|
||||
}
|
||||
request->send(response);
|
||||
}
|
||||
|
@ -26,8 +26,6 @@
|
||||
#define os_strlen strlen
|
||||
#endif
|
||||
|
||||
static const String SharedEmptyString = String();
|
||||
|
||||
#define __is_param_char(c) ((c) && ((c) != '{') && ((c) != '[') && ((c) != '&') && ((c) != '='))
|
||||
|
||||
enum { PARSE_REQ_START, PARSE_REQ_HEADERS, PARSE_REQ_BODY, PARSE_REQ_END, PARSE_REQ_FAIL };
|
||||
@ -53,9 +51,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
|
||||
, _expectingContinue(false)
|
||||
, _contentLength(0)
|
||||
, _parsedLength(0)
|
||||
, _headers(LinkedList<AsyncWebHeader *>([](AsyncWebHeader *h){ delete h; }))
|
||||
, _params(LinkedList<AsyncWebParameter *>([](AsyncWebParameter *p){ delete p; }))
|
||||
, _pathParams(LinkedList<String *>([](String *p){ delete p; }))
|
||||
, _multiParseState(0)
|
||||
, _boundaryPosition(0)
|
||||
, _itemStartIndex(0)
|
||||
@ -78,12 +74,12 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
|
||||
}
|
||||
|
||||
AsyncWebServerRequest::~AsyncWebServerRequest(){
|
||||
_headers.free();
|
||||
_headers.clear();
|
||||
|
||||
_params.free();
|
||||
_pathParams.free();
|
||||
_pathParams.clear();
|
||||
|
||||
_interestingHeaders.free();
|
||||
_interestingHeaders.clear();
|
||||
|
||||
if(_response != NULL){
|
||||
delete _response;
|
||||
@ -96,6 +92,11 @@ AsyncWebServerRequest::~AsyncWebServerRequest(){
|
||||
if(_tempFile){
|
||||
_tempFile.close();
|
||||
}
|
||||
|
||||
if(_itemBuffer){
|
||||
free(_itemBuffer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::_onData(void *buf, size_t len){
|
||||
@ -143,9 +144,9 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
|
||||
_parsedLength += len;
|
||||
} else {
|
||||
if(_parsedLength == 0){
|
||||
if(_contentType.startsWith("application/x-www-form-urlencoded")){
|
||||
if(_contentType.startsWith(F("application/x-www-form-urlencoded"))){
|
||||
_isPlainPost = true;
|
||||
} else if(_contentType == "text/plain" && __is_param_char(((char*)buf)[0])){
|
||||
} else if(_contentType == F("text/plain") && __is_param_char(((char*)buf)[0])){
|
||||
size_t i = 0;
|
||||
while (i<len && __is_param_char(((char*)buf)[i++]));
|
||||
if(i < len && ((char*)buf)[i-1] == '='){
|
||||
@ -179,18 +180,34 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::_removeNotInterestingHeaders(){
|
||||
if (_interestingHeaders.containsIgnoreCase("ANY")) return; // nothing to do
|
||||
for(const auto& header: _headers){
|
||||
if(!_interestingHeaders.containsIgnoreCase(header->name().c_str())){
|
||||
_headers.remove(header);
|
||||
}
|
||||
if (std::any_of(std::begin(_interestingHeaders), std::end(_interestingHeaders),
|
||||
[](const String &str){ return str.equalsIgnoreCase(F("ANY")); }))
|
||||
return; // nothing to do
|
||||
|
||||
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(){
|
||||
//os_printf("p\n");
|
||||
if(_response != NULL && _client != NULL && _client->canSend() && !_response->_finished()){
|
||||
_response->_ack(this, 0, 0);
|
||||
if(_response != NULL && _client != NULL && _client->canSend()){
|
||||
if(!_response->_finished()){
|
||||
_response->_ack(this, 0, 0);
|
||||
} else {
|
||||
AsyncWebServerResponse* r = _response;
|
||||
_response = NULL;
|
||||
delete r;
|
||||
|
||||
_client->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,10 +216,12 @@ void AsyncWebServerRequest::_onAck(size_t len, uint32_t time){
|
||||
if(_response != NULL){
|
||||
if(!_response->_finished()){
|
||||
_response->_ack(this, len, time);
|
||||
} else {
|
||||
} else if(_response->_finished()){
|
||||
AsyncWebServerResponse* r = _response;
|
||||
_response = NULL;
|
||||
delete r;
|
||||
|
||||
_client->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -234,7 +253,7 @@ void AsyncWebServerRequest::_addParam(AsyncWebParameter *p){
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::_addPathParam(const char *p){
|
||||
_pathParams.add(new String(p));
|
||||
_pathParams.emplace_back(p);
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::_addGetParams(const String& params){
|
||||
@ -259,23 +278,23 @@ bool AsyncWebServerRequest::_parseReqHead(){
|
||||
String u = _temp.substring(m.length()+1, index);
|
||||
_temp = _temp.substring(index+1);
|
||||
|
||||
if(m == "GET"){
|
||||
if(m == F("GET")){
|
||||
_method = HTTP_GET;
|
||||
} else if(m == "POST"){
|
||||
} else if(m == F("POST")){
|
||||
_method = HTTP_POST;
|
||||
} else if(m == "DELETE"){
|
||||
} else if(m == F("DELETE")){
|
||||
_method = HTTP_DELETE;
|
||||
} else if(m == "PUT"){
|
||||
} else if(m == F("PUT")){
|
||||
_method = HTTP_PUT;
|
||||
} else if(m == "PATCH"){
|
||||
} else if(m == F("PATCH")){
|
||||
_method = HTTP_PATCH;
|
||||
} else if(m == "HEAD"){
|
||||
} else if(m == F("HEAD")){
|
||||
_method = HTTP_HEAD;
|
||||
} else if(m == "OPTIONS"){
|
||||
} else if(m == F("OPTIONS")){
|
||||
_method = HTTP_OPTIONS;
|
||||
}
|
||||
|
||||
String g = String();
|
||||
String g;
|
||||
index = u.indexOf('?');
|
||||
if(index > 0){
|
||||
g = u.substring(index +1);
|
||||
@ -284,14 +303,14 @@ bool AsyncWebServerRequest::_parseReqHead(){
|
||||
_url = urlDecode(u);
|
||||
_addGetParams(g);
|
||||
|
||||
if(!_temp.startsWith("HTTP/1.0"))
|
||||
if(!_temp.startsWith(F("HTTP/1.0")))
|
||||
_version = 1;
|
||||
|
||||
_temp = String();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool strContains(String src, String find, bool mindcase = true) {
|
||||
bool strContains(const String &src, const String &find, bool mindcase = true) {
|
||||
int pos=0, i=0;
|
||||
const int slen = src.length();
|
||||
const int flen = find.length();
|
||||
@ -301,7 +320,10 @@ bool strContains(String src, String find, bool mindcase = true) {
|
||||
for (i=0; i < flen; i++) {
|
||||
if (mindcase) {
|
||||
if (src[pos+i] != find[i]) i = flen + 1; // no match
|
||||
} else if (tolower(src[pos+i]) != tolower(find[i])) i = flen + 1; // no match
|
||||
}
|
||||
else if (tolower(src[pos+i]) != tolower(find[i])) {
|
||||
i = flen + 1; // no match
|
||||
}
|
||||
}
|
||||
if (i == flen) return true;
|
||||
pos++;
|
||||
@ -316,36 +338,36 @@ bool AsyncWebServerRequest::_parseReqHeader(){
|
||||
String value = _temp.substring(index + 2);
|
||||
if(name.equalsIgnoreCase("Host")){
|
||||
_host = value;
|
||||
} else if(name.equalsIgnoreCase("Content-Type")){
|
||||
_contentType = value.substring(0, value.indexOf(';'));
|
||||
if (value.startsWith("multipart/")){
|
||||
} else if(name.equalsIgnoreCase(F("Content-Type"))){
|
||||
_contentType = value.substring(0, value.indexOf(';'));
|
||||
if (value.startsWith(F("multipart/"))){
|
||||
_boundary = value.substring(value.indexOf('=')+1);
|
||||
_boundary.replace("\"","");
|
||||
_boundary.replace(String('"'), String());
|
||||
_isMultipart = true;
|
||||
}
|
||||
} else if(name.equalsIgnoreCase("Content-Length")){
|
||||
} else if(name.equalsIgnoreCase(F("Content-Length"))){
|
||||
_contentLength = atoi(value.c_str());
|
||||
} else if(name.equalsIgnoreCase("Expect") && value == "100-continue"){
|
||||
} else if(name.equalsIgnoreCase(F("Expect")) && value == F("100-continue")){
|
||||
_expectingContinue = true;
|
||||
} else if(name.equalsIgnoreCase("Authorization")){
|
||||
if(value.length() > 5 && value.substring(0,5).equalsIgnoreCase("Basic")){
|
||||
} else if(name.equalsIgnoreCase(F("Authorization"))){
|
||||
if(value.length() > 5 && value.substring(0,5).equalsIgnoreCase(F("Basic"))){
|
||||
_authorization = value.substring(6);
|
||||
} else if(value.length() > 6 && value.substring(0,6).equalsIgnoreCase("Digest")){
|
||||
} else if(value.length() > 6 && value.substring(0,6).equalsIgnoreCase(F("Digest"))){
|
||||
_isDigest = true;
|
||||
_authorization = value.substring(7);
|
||||
}
|
||||
} else {
|
||||
if(name.equalsIgnoreCase("Upgrade") && value.equalsIgnoreCase("websocket")){
|
||||
if(name.equalsIgnoreCase(F("Upgrade")) && value.equalsIgnoreCase(F("websocket"))){
|
||||
// WebSocket request can be uniquely identified by header: [Upgrade: websocket]
|
||||
_reqconntype = RCT_WS;
|
||||
} else {
|
||||
if(name.equalsIgnoreCase("Accept") && strContains(value, "text/event-stream", false)){
|
||||
if(name.equalsIgnoreCase(F("Accept")) && strContains(value, F("text/event-stream"), false)){
|
||||
// WebEvent request can be uniquely identified by header: [Accept: text/event-stream]
|
||||
_reqconntype = RCT_EVENT;
|
||||
}
|
||||
}
|
||||
}
|
||||
_headers.add(new AsyncWebHeader(name, value));
|
||||
_headers.emplace_back(name, value);
|
||||
}
|
||||
_temp = String();
|
||||
return true;
|
||||
@ -355,9 +377,9 @@ void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data){
|
||||
if(data && (char)data != '&')
|
||||
_temp += (char)data;
|
||||
if(!data || (char)data == '&' || _parsedLength == _contentLength){
|
||||
String name = "body";
|
||||
String name = F("body");
|
||||
String value = _temp;
|
||||
if(!_temp.startsWith("{") && !_temp.startsWith("[") && _temp.indexOf('=') > 0){
|
||||
if(!_temp.startsWith(String('{')) && !_temp.startsWith(String('[')) && _temp.indexOf('=') > 0){
|
||||
name = _temp.substring(0, _temp.indexOf('='));
|
||||
value = _temp.substring(_temp.indexOf('=') + 1);
|
||||
}
|
||||
@ -431,17 +453,17 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
|
||||
_temp += (char)data;
|
||||
if((char)data == '\n'){
|
||||
if(_temp.length()){
|
||||
if(_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase("Content-Type")){
|
||||
if(_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase(F("Content-Type"))){
|
||||
_itemType = _temp.substring(14);
|
||||
_itemIsFile = true;
|
||||
} else if(_temp.length() > 19 && _temp.substring(0, 19).equalsIgnoreCase("Content-Disposition")){
|
||||
} else if(_temp.length() > 19 && _temp.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))){
|
||||
_temp = _temp.substring(_temp.indexOf(';') + 2);
|
||||
while(_temp.indexOf(';') > 0){
|
||||
String name = _temp.substring(0, _temp.indexOf('='));
|
||||
String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.indexOf(';') - 1);
|
||||
if(name == "name"){
|
||||
if(name == F("name")){
|
||||
_itemName = nameVal;
|
||||
} else if(name == "filename"){
|
||||
} else if(name == F("filename")){
|
||||
_itemFilename = nameVal;
|
||||
_itemIsFile = true;
|
||||
}
|
||||
@ -449,9 +471,9 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
|
||||
}
|
||||
String name = _temp.substring(0, _temp.indexOf('='));
|
||||
String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.length() - 1);
|
||||
if(name == "name"){
|
||||
if(name == F("name")){
|
||||
_itemName = nameVal;
|
||||
} else if(name == "filename"){
|
||||
} else if(name == F("filename")){
|
||||
_itemFilename = nameVal;
|
||||
_itemIsFile = true;
|
||||
}
|
||||
@ -570,8 +592,8 @@ void AsyncWebServerRequest::_parseLine(){
|
||||
_server->_attachHandler(this);
|
||||
_removeNotInterestingHeaders();
|
||||
if(_expectingContinue){
|
||||
const char * response = "HTTP/1.1 100 Continue\r\n\r\n";
|
||||
_client->write(response, os_strlen(response));
|
||||
String response = F("HTTP/1.1 100 Continue\r\n\r\n");
|
||||
_client->write(response.c_str(), response.length());
|
||||
}
|
||||
//check handler for authentication
|
||||
if(_contentLength){
|
||||
@ -586,12 +608,12 @@ void AsyncWebServerRequest::_parseLine(){
|
||||
}
|
||||
|
||||
size_t AsyncWebServerRequest::headers() const{
|
||||
return _headers.length();
|
||||
return _headers.size();
|
||||
}
|
||||
|
||||
bool AsyncWebServerRequest::hasHeader(const String& name) const {
|
||||
for(const auto& h: _headers){
|
||||
if(h->name().equalsIgnoreCase(name)){
|
||||
if(h.name().equalsIgnoreCase(name)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -599,51 +621,67 @@ bool AsyncWebServerRequest::hasHeader(const String& name) const {
|
||||
}
|
||||
|
||||
bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper * data) const {
|
||||
PGM_P p = reinterpret_cast<PGM_P>(data);
|
||||
size_t n = 0;
|
||||
while (1) {
|
||||
if (pgm_read_byte(p+n) == 0) break;
|
||||
n += 1;
|
||||
}
|
||||
char * name = (char*) malloc(n+1);
|
||||
name[n] = 0;
|
||||
if (name) {
|
||||
for(size_t b=0; b<n; b++)
|
||||
name[b] = pgm_read_byte(p++);
|
||||
bool result = hasHeader( String(name) );
|
||||
free(name);
|
||||
return result;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return hasHeader(String(data));
|
||||
}
|
||||
|
||||
AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) const {
|
||||
for(const auto& h: _headers){
|
||||
if(h->name().equalsIgnoreCase(name)){
|
||||
return h;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) {
|
||||
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) const {
|
||||
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);
|
||||
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;
|
||||
strcpy_P(name, p);
|
||||
AsyncWebHeader* result = getHeader( String(name));
|
||||
free(name);
|
||||
return result;
|
||||
} else {
|
||||
return nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const {
|
||||
auto header = _headers.nth(num);
|
||||
return header ? *header : nullptr;
|
||||
const AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) const {
|
||||
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);
|
||||
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 {
|
||||
@ -660,19 +698,7 @@ bool AsyncWebServerRequest::hasParam(const String& name, bool post, bool file) c
|
||||
}
|
||||
|
||||
bool AsyncWebServerRequest::hasParam(const __FlashStringHelper * data, bool post, bool file) const {
|
||||
PGM_P p = reinterpret_cast<PGM_P>(data);
|
||||
size_t n = strlen_P(p);
|
||||
|
||||
char * name = (char*) malloc(n+1);
|
||||
name[n] = 0;
|
||||
if (name) {
|
||||
strcpy_P(name,p);
|
||||
bool result = hasParam( name, post, file);
|
||||
free(name);
|
||||
return result;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return hasParam(String(data).c_str(), post, file);
|
||||
}
|
||||
|
||||
AsyncWebParameter* AsyncWebServerRequest::getParam(const String& name, bool post, bool file) const {
|
||||
@ -685,17 +711,7 @@ AsyncWebParameter* AsyncWebServerRequest::getParam(const String& name, bool post
|
||||
}
|
||||
|
||||
AsyncWebParameter* AsyncWebServerRequest::getParam(const __FlashStringHelper * data, bool post, bool file) const {
|
||||
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);
|
||||
AsyncWebParameter* result = getParam(name, post, file);
|
||||
free(name);
|
||||
return result;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
return getParam(String(data).c_str(), post, file);
|
||||
}
|
||||
|
||||
AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const {
|
||||
@ -704,8 +720,9 @@ AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const {
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::addInterestingHeader(const String& name){
|
||||
if(!_interestingHeaders.containsIgnoreCase(name))
|
||||
_interestingHeaders.add(name);
|
||||
if(std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders),
|
||||
[&name](const String &str){ return str.equalsIgnoreCase(name); }))
|
||||
_interestingHeaders.push_back(name);
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::send(AsyncWebServerResponse *response){
|
||||
@ -731,7 +748,7 @@ AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const St
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){
|
||||
if(fs.exists(path) || (!download && fs.exists(path+".gz")))
|
||||
if(fs.exists(path) || (!download && fs.exists(path+F(".gz"))))
|
||||
return new AsyncFileResponse(fs, path, contentType, download, callback);
|
||||
return NULL;
|
||||
}
|
||||
@ -773,7 +790,7 @@ void AsyncWebServerRequest::send(int code, const String& contentType, const Stri
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::send(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){
|
||||
if(fs.exists(path) || (!download && fs.exists(path+".gz"))){
|
||||
if(fs.exists(path) || (!download && fs.exists(path+F(".gz")))){
|
||||
send(beginResponse(fs, path, contentType, download, callback));
|
||||
} else send(404);
|
||||
}
|
||||
@ -806,7 +823,7 @@ void AsyncWebServerRequest::send_P(int code, const String& contentType, PGM_P co
|
||||
|
||||
void AsyncWebServerRequest::redirect(const String& url){
|
||||
AsyncWebServerResponse * response = beginResponse(302);
|
||||
response->addHeader("Location",url);
|
||||
response->addHeader(F("Location"), url);
|
||||
send(response);
|
||||
}
|
||||
|
||||
@ -828,12 +845,12 @@ bool AsyncWebServerRequest::authenticate(const char * hash){
|
||||
|
||||
if(_isDigest){
|
||||
String hStr = String(hash);
|
||||
int separator = hStr.indexOf(":");
|
||||
int separator = hStr.indexOf(':');
|
||||
if(separator <= 0)
|
||||
return false;
|
||||
String username = hStr.substring(0, separator);
|
||||
hStr = hStr.substring(separator + 1);
|
||||
separator = hStr.indexOf(":");
|
||||
separator = hStr.indexOf(':');
|
||||
if(separator <= 0)
|
||||
return false;
|
||||
String realm = hStr.substring(0, separator);
|
||||
@ -847,16 +864,16 @@ bool AsyncWebServerRequest::authenticate(const char * hash){
|
||||
void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDigest){
|
||||
AsyncWebServerResponse * r = beginResponse(401);
|
||||
if(!isDigest && realm == NULL){
|
||||
r->addHeader("WWW-Authenticate", "Basic realm=\"Login Required\"");
|
||||
r->addHeader(F("WWW-Authenticate"), F("Basic realm=\"Login Required\""));
|
||||
} else if(!isDigest){
|
||||
String header = "Basic realm=\"";
|
||||
String header = F("Basic realm=\"");
|
||||
header.concat(realm);
|
||||
header.concat("\"");
|
||||
r->addHeader("WWW-Authenticate", header);
|
||||
header += '"';
|
||||
r->addHeader(F("WWW-Authenticate"), header);
|
||||
} else {
|
||||
String header = "Digest ";
|
||||
String header = F("Digest ");
|
||||
header.concat(requestDigestAuthentication(realm));
|
||||
r->addHeader("WWW-Authenticate", header);
|
||||
r->addHeader(F("WWW-Authenticate"), header);
|
||||
}
|
||||
send(r);
|
||||
}
|
||||
@ -871,17 +888,7 @@ bool AsyncWebServerRequest::hasArg(const char* name) const {
|
||||
}
|
||||
|
||||
bool AsyncWebServerRequest::hasArg(const __FlashStringHelper * data) const {
|
||||
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);
|
||||
bool result = hasArg( name );
|
||||
free(name);
|
||||
return result;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return hasArg(String(data).c_str());
|
||||
}
|
||||
|
||||
|
||||
@ -891,22 +898,11 @@ const String& AsyncWebServerRequest::arg(const String& name) const {
|
||||
return arg->value();
|
||||
}
|
||||
}
|
||||
return SharedEmptyString;
|
||||
return emptyString;
|
||||
}
|
||||
|
||||
const String& AsyncWebServerRequest::arg(const __FlashStringHelper * data) const {
|
||||
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);
|
||||
const String & result = arg( String(name) );
|
||||
free(name);
|
||||
return result;
|
||||
} else {
|
||||
return SharedEmptyString;
|
||||
}
|
||||
|
||||
return arg(String(data).c_str());
|
||||
}
|
||||
|
||||
const String& AsyncWebServerRequest::arg(size_t i) const {
|
||||
@ -918,38 +914,27 @@ const String& AsyncWebServerRequest::argName(size_t i) const {
|
||||
}
|
||||
|
||||
const String& AsyncWebServerRequest::pathArg(size_t i) const {
|
||||
auto param = _pathParams.nth(i);
|
||||
return param ? **param : SharedEmptyString;
|
||||
return i < _pathParams.size() ? _pathParams[i] : emptyString;
|
||||
}
|
||||
|
||||
const String& AsyncWebServerRequest::header(const char* name) const {
|
||||
AsyncWebHeader* h = getHeader(String(name));
|
||||
return h ? h->value() : SharedEmptyString;
|
||||
const AsyncWebHeader* h = getHeader(String(name));
|
||||
return h ? h->value() : emptyString;
|
||||
}
|
||||
|
||||
const String& AsyncWebServerRequest::header(const __FlashStringHelper * data) const {
|
||||
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);
|
||||
const String & result = header( (const char *)name );
|
||||
free(name);
|
||||
return result;
|
||||
} else {
|
||||
return SharedEmptyString;
|
||||
}
|
||||
};
|
||||
return header(String(data).c_str());
|
||||
};
|
||||
|
||||
|
||||
const String& AsyncWebServerRequest::header(size_t i) const {
|
||||
AsyncWebHeader* h = getHeader(i);
|
||||
return h ? h->value() : SharedEmptyString;
|
||||
const AsyncWebHeader* h = getHeader(i);
|
||||
return h ? h->value() : emptyString;
|
||||
}
|
||||
|
||||
const String& AsyncWebServerRequest::headerName(size_t i) const {
|
||||
AsyncWebHeader* h = getHeader(i);
|
||||
return h ? h->name() : SharedEmptyString;
|
||||
const AsyncWebHeader* h = getHeader(i);
|
||||
return h ? h->name() : emptyString;
|
||||
}
|
||||
|
||||
String AsyncWebServerRequest::urlDecode(const String& text) const {
|
||||
@ -976,26 +961,26 @@ String AsyncWebServerRequest::urlDecode(const String& text) const {
|
||||
}
|
||||
|
||||
|
||||
const char * AsyncWebServerRequest::methodToString() const {
|
||||
if(_method == HTTP_ANY) return "ANY";
|
||||
else if(_method & HTTP_GET) return "GET";
|
||||
else if(_method & HTTP_POST) return "POST";
|
||||
else if(_method & HTTP_DELETE) return "DELETE";
|
||||
else if(_method & HTTP_PUT) return "PUT";
|
||||
else if(_method & HTTP_PATCH) return "PATCH";
|
||||
else if(_method & HTTP_HEAD) return "HEAD";
|
||||
else if(_method & HTTP_OPTIONS) return "OPTIONS";
|
||||
return "UNKNOWN";
|
||||
const __FlashStringHelper *AsyncWebServerRequest::methodToString() const {
|
||||
if(_method == HTTP_ANY) return F("ANY");
|
||||
else if(_method & HTTP_GET) return F("GET");
|
||||
else if(_method & HTTP_POST) return F("POST");
|
||||
else if(_method & HTTP_DELETE) return F("DELETE");
|
||||
else if(_method & HTTP_PUT) return F("PUT");
|
||||
else if(_method & HTTP_PATCH) return F("PATCH");
|
||||
else if(_method & HTTP_HEAD) return F("HEAD");
|
||||
else if(_method & HTTP_OPTIONS) return F("OPTIONS");
|
||||
return F("UNKNOWN");
|
||||
}
|
||||
|
||||
const char *AsyncWebServerRequest::requestedConnTypeToString() const {
|
||||
const __FlashStringHelper *AsyncWebServerRequest::requestedConnTypeToString() const {
|
||||
switch (_reqconntype) {
|
||||
case RCT_NOT_USED: return "RCT_NOT_USED";
|
||||
case RCT_DEFAULT: return "RCT_DEFAULT";
|
||||
case RCT_HTTP: return "RCT_HTTP";
|
||||
case RCT_WS: return "RCT_WS";
|
||||
case RCT_EVENT: return "RCT_EVENT";
|
||||
default: return "ERROR";
|
||||
case RCT_NOT_USED: return F("RCT_NOT_USED");
|
||||
case RCT_DEFAULT: return F("RCT_DEFAULT");
|
||||
case RCT_HTTP: return F("RCT_HTTP");
|
||||
case RCT_WS: return F("RCT_WS");
|
||||
case RCT_EVENT: return F("RCT_EVENT");
|
||||
default: return F("ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,8 @@
|
||||
#undef max
|
||||
#endif
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
// It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max.
|
||||
|
||||
class AsyncBasicResponse: public AsyncWebServerResponse {
|
||||
@ -122,7 +124,7 @@ class cbuf;
|
||||
|
||||
class AsyncResponseStream: public AsyncAbstractResponse, public Print {
|
||||
private:
|
||||
cbuf *_content;
|
||||
std::unique_ptr<cbuf> _content;
|
||||
public:
|
||||
AsyncResponseStream(const String& contentType, size_t bufferSize);
|
||||
~AsyncResponseStream();
|
||||
|
@ -37,54 +37,57 @@ void* memchr(void* ptr, int ch, size_t count)
|
||||
* 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 "Continue";
|
||||
case 101: return "Switching Protocols";
|
||||
case 200: return "OK";
|
||||
case 201: return "Created";
|
||||
case 202: return "Accepted";
|
||||
case 203: return "Non-Authoritative Information";
|
||||
case 204: return "No Content";
|
||||
case 205: return "Reset Content";
|
||||
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";
|
||||
default: return "";
|
||||
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)
|
||||
, _headers(LinkedList<AsyncWebHeader *>([](AsyncWebHeader *h){ delete h; }))
|
||||
, _contentType()
|
||||
, _contentLength(0)
|
||||
, _sendContentLength(true)
|
||||
@ -95,14 +98,12 @@ AsyncWebServerResponse::AsyncWebServerResponse()
|
||||
, _writtenLength(0)
|
||||
, _state(RESPONSE_SETUP)
|
||||
{
|
||||
for(auto header: DefaultHeaders::Instance()) {
|
||||
_headers.add(new AsyncWebHeader(header->name(), header->value()));
|
||||
for(const auto &header: DefaultHeaders::Instance()) {
|
||||
_headers.emplace_back(header);
|
||||
}
|
||||
}
|
||||
|
||||
AsyncWebServerResponse::~AsyncWebServerResponse(){
|
||||
_headers.free();
|
||||
}
|
||||
AsyncWebServerResponse::~AsyncWebServerResponse() = default;
|
||||
|
||||
void AsyncWebServerResponse::setCode(int code){
|
||||
if(_state == RESPONSE_SETUP)
|
||||
@ -120,38 +121,38 @@ void AsyncWebServerResponse::setContentType(const String& type){
|
||||
}
|
||||
|
||||
void AsyncWebServerResponse::addHeader(const String& name, const String& value){
|
||||
_headers.add(new AsyncWebHeader(name, value));
|
||||
_headers.emplace_back(name, value);
|
||||
}
|
||||
|
||||
String AsyncWebServerResponse::_assembleHead(uint8_t version){
|
||||
if(version){
|
||||
addHeader("Accept-Ranges","none");
|
||||
addHeader(F("Accept-Ranges"), F("none"));
|
||||
if(_chunked)
|
||||
addHeader("Transfer-Encoding","chunked");
|
||||
addHeader(F("Transfer-Encoding"), F("chunked"));
|
||||
}
|
||||
String out = String();
|
||||
int bufSize = 300;
|
||||
char buf[bufSize];
|
||||
|
||||
snprintf(buf, bufSize, "HTTP/1.%d %d %s\r\n", version, _code, _responseCodeToString(_code));
|
||||
snprintf_P(buf, bufSize, PSTR("HTTP/1.%d %d %s\r\n"), version, _code, _responseCodeToString(_code));
|
||||
out.concat(buf);
|
||||
|
||||
if(_sendContentLength) {
|
||||
snprintf(buf, bufSize, "Content-Length: %d\r\n", _contentLength);
|
||||
snprintf_P(buf, bufSize, PSTR("Content-Length: %d\r\n"), _contentLength);
|
||||
out.concat(buf);
|
||||
}
|
||||
if(_contentType.length()) {
|
||||
snprintf(buf, bufSize, "Content-Type: %s\r\n", _contentType.c_str());
|
||||
snprintf_P(buf, bufSize, PSTR("Content-Type: %s\r\n"), _contentType.c_str());
|
||||
out.concat(buf);
|
||||
}
|
||||
|
||||
for(const auto& header: _headers){
|
||||
snprintf(buf, bufSize, "%s: %s\r\n", header->name().c_str(), header->value().c_str());
|
||||
snprintf_P(buf, bufSize, PSTR("%s: %s\r\n"), header.name().c_str(), header.value().c_str());
|
||||
out.concat(buf);
|
||||
}
|
||||
_headers.free();
|
||||
_headers.clear();
|
||||
|
||||
out.concat("\r\n");
|
||||
out.concat(F("\r\n"));
|
||||
_headLength = out.length();
|
||||
return out;
|
||||
}
|
||||
@ -173,9 +174,9 @@ AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, cons
|
||||
if(_content.length()){
|
||||
_contentLength = _content.length();
|
||||
if(!_contentType.length())
|
||||
_contentType = "text/plain";
|
||||
_contentType = F("text/plain");
|
||||
}
|
||||
addHeader("Connection","close");
|
||||
addHeader(F("Connection"), F("close"));
|
||||
}
|
||||
|
||||
void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){
|
||||
@ -255,7 +256,7 @@ AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback): _ca
|
||||
}
|
||||
|
||||
void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){
|
||||
addHeader("Connection","close");
|
||||
addHeader(F("Connection"), F("close"));
|
||||
_head = _assembleHead(request->version());
|
||||
_state = RESPONSE_HEADERS;
|
||||
_ack(request, 0, 0);
|
||||
@ -317,7 +318,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
outLen = sprintf((char*)buf+headLen, "%x", readLen) + headLen;
|
||||
outLen = sprintf_P((char*)buf+headLen, PSTR("%x"), readLen) + headLen;
|
||||
while(outLen < headLen + 4) buf[outLen++] = ' ';
|
||||
buf[outLen++] = '\r';
|
||||
buf[outLen++] = '\n';
|
||||
@ -396,7 +397,7 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
|
||||
// If closing placeholder is found:
|
||||
if(pTemplateEnd) {
|
||||
// prepare argument to callback
|
||||
const size_t paramNameLength = std::min(sizeof(buf) - 1, (unsigned int)(pTemplateEnd - pTemplateStart - 1));
|
||||
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;
|
||||
@ -483,43 +484,48 @@ AsyncFileResponse::~AsyncFileResponse(){
|
||||
}
|
||||
|
||||
void AsyncFileResponse::_setContentType(const String& path){
|
||||
if (path.endsWith(".html")) _contentType = "text/html";
|
||||
else if (path.endsWith(".htm")) _contentType = "text/html";
|
||||
else if (path.endsWith(".css")) _contentType = "text/css";
|
||||
else if (path.endsWith(".json")) _contentType = "application/json";
|
||||
else if (path.endsWith(".js")) _contentType = "application/javascript";
|
||||
else if (path.endsWith(".png")) _contentType = "image/png";
|
||||
else if (path.endsWith(".gif")) _contentType = "image/gif";
|
||||
else if (path.endsWith(".jpg")) _contentType = "image/jpeg";
|
||||
else if (path.endsWith(".ico")) _contentType = "image/x-icon";
|
||||
else if (path.endsWith(".svg")) _contentType = "image/svg+xml";
|
||||
else if (path.endsWith(".eot")) _contentType = "font/eot";
|
||||
else if (path.endsWith(".woff")) _contentType = "font/woff";
|
||||
else if (path.endsWith(".woff2")) _contentType = "font/woff2";
|
||||
else if (path.endsWith(".ttf")) _contentType = "font/ttf";
|
||||
else if (path.endsWith(".xml")) _contentType = "text/xml";
|
||||
else if (path.endsWith(".pdf")) _contentType = "application/pdf";
|
||||
else if (path.endsWith(".zip")) _contentType = "application/zip";
|
||||
else if(path.endsWith(".gz")) _contentType = "application/x-gzip";
|
||||
else _contentType = "text/plain";
|
||||
#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+".gz")){
|
||||
_path = _path+".gz";
|
||||
addHeader("Content-Encoding", "gzip");
|
||||
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, "r");
|
||||
_content = fs.open(_path, fs::FileOpenMode::read);
|
||||
_contentLength = _content.size();
|
||||
|
||||
if(contentType == "")
|
||||
if(contentType.length() == 0)
|
||||
_setContentType(path);
|
||||
else
|
||||
_contentType = contentType;
|
||||
@ -530,20 +536,20 @@ AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& c
|
||||
|
||||
if(download) {
|
||||
// set filename and force download
|
||||
snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename);
|
||||
snprintf_P(buf, sizeof (buf), PSTR("attachment; filename=\"%s\""), filename);
|
||||
} else {
|
||||
// set filename and force rendering
|
||||
snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename);
|
||||
snprintf_P(buf, sizeof (buf), PSTR("inline; filename=\"%s\""), filename);
|
||||
}
|
||||
addHeader("Content-Disposition", buf);
|
||||
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(".gz") && !path.endsWith(".gz")){
|
||||
addHeader("Content-Encoding", "gzip");
|
||||
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;
|
||||
@ -552,7 +558,7 @@ AsyncFileResponse::AsyncFileResponse(File content, const String& path, const Str
|
||||
_content = content;
|
||||
_contentLength = _content.size();
|
||||
|
||||
if(contentType == "")
|
||||
if(contentType.length() == 0)
|
||||
_setContentType(path);
|
||||
else
|
||||
_contentType = contentType;
|
||||
@ -562,11 +568,11 @@ AsyncFileResponse::AsyncFileResponse(File content, const String& path, const Str
|
||||
char* filename = (char*)path.c_str() + filenameStart;
|
||||
|
||||
if(download) {
|
||||
snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename);
|
||||
snprintf_P(buf, sizeof (buf), PSTR("attachment; filename=\"%s\""), filename);
|
||||
} else {
|
||||
snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename);
|
||||
snprintf_P(buf, sizeof (buf), PSTR("inline; filename=\"%s\""), filename);
|
||||
}
|
||||
addHeader("Content-Disposition", buf);
|
||||
addHeader(F("Content-Disposition"), buf);
|
||||
}
|
||||
|
||||
size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){
|
||||
@ -666,16 +672,15 @@ size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){
|
||||
* Response Stream (You can print/write/printf to it, up to the contentLen bytes)
|
||||
* */
|
||||
|
||||
AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize){
|
||||
AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize)
|
||||
{
|
||||
_code = 200;
|
||||
_contentLength = 0;
|
||||
_contentType = contentType;
|
||||
_content = new cbuf(bufferSize);
|
||||
_content = std::unique_ptr<cbuf>(new cbuf(bufferSize)); //std::make_unique<cbuf>(bufferSize);
|
||||
}
|
||||
|
||||
AsyncResponseStream::~AsyncResponseStream(){
|
||||
delete _content;
|
||||
}
|
||||
AsyncResponseStream::~AsyncResponseStream() = default;
|
||||
|
||||
size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen){
|
||||
return _content->read((char*)buf, maxLen);
|
||||
|
@ -29,11 +29,16 @@ bool ON_AP_FILTER(AsyncWebServerRequest *request) {
|
||||
return WiFi.localIP() != request->client()->localIP();
|
||||
}
|
||||
|
||||
#ifndef HAVE_FS_FILE_OPEN_MODE
|
||||
const char *fs::FileOpenMode::read = "r";
|
||||
const char *fs::FileOpenMode::write = "w";
|
||||
const char *fs::FileOpenMode::append = "a";
|
||||
#endif
|
||||
|
||||
AsyncWebServer::AsyncWebServer(uint16_t port)
|
||||
: _server(port)
|
||||
, _rewrites(LinkedList<AsyncWebRewrite*>(nullptr))
|
||||
, _handlers(LinkedList<AsyncWebHandler*>(nullptr))
|
||||
, _rewrites(LinkedList<AsyncWebRewrite*>([](AsyncWebRewrite* r){ delete r; }))
|
||||
, _handlers(LinkedList<AsyncWebHandler*>([](AsyncWebHandler* h){ delete h; }))
|
||||
{
|
||||
_catchAllHandler = new AsyncCallbackWebHandler();
|
||||
if(_catchAllHandler == NULL)
|
||||
@ -52,7 +57,7 @@ AsyncWebServer::AsyncWebServer(uint16_t port)
|
||||
}
|
||||
|
||||
AsyncWebServer::~AsyncWebServer(){
|
||||
reset();
|
||||
reset();
|
||||
end();
|
||||
if(_catchAllHandler) delete _catchAllHandler;
|
||||
}
|
||||
@ -118,8 +123,8 @@ void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
request->addInterestingHeader("ANY");
|
||||
|
||||
request->addInterestingHeader(F("ANY"));
|
||||
request->setHandler(_catchAllHandler);
|
||||
}
|
||||
|
||||
@ -183,7 +188,7 @@ void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn){
|
||||
void AsyncWebServer::reset(){
|
||||
_rewrites.free();
|
||||
_handlers.free();
|
||||
|
||||
|
||||
if (_catchAllHandler != NULL){
|
||||
_catchAllHandler->onRequest(NULL);
|
||||
_catchAllHandler->onUpload(NULL);
|
||||
|
581
src/edit.htm
Normal file
581
src/edit.htm
Normal file
@ -0,0 +1,581 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||
<title>ESP Editor</title>
|
||||
<link rel="apple-touch-icon" href="/ace.ico" type="image/x-icon" />
|
||||
<link rel="shortcut icon" href="/ace.ico" type="image/x-icon" />
|
||||
<link rel="icon" href="/ace.ico" type="image/x-icon" />
|
||||
<style type="text/css" media="screen">
|
||||
label {
|
||||
font-size: 12px;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.cm {
|
||||
z-index: 300;
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
border: 1px solid #444;
|
||||
background-color: #f5f5f5;
|
||||
display: none;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
|
||||
font-size: 12px;
|
||||
font-family: sans-serif;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.cm ul {
|
||||
list-style: none;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.cm li {
|
||||
position: relative;
|
||||
min-width: 60px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cm span {
|
||||
color: #444;
|
||||
display: inline-block;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.cm li:hover {
|
||||
background: #444;
|
||||
}
|
||||
|
||||
.cm li:hover span {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.tvu li,
|
||||
.tvu ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.tvu input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.tvu {
|
||||
font: 400 12px Verdana, Arial, Sans-serif;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
color: #444;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.tvu span {
|
||||
margin-bottom: 5px;
|
||||
padding: 0 0 0 18px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADoSURBVBgZBcExblNBGAbA2ceegTRBuIKOgiihSZNTcC5LUHAihNJR0kGKCDcYJY6D3/77MdOinTvzAgCw8ysThIvn/VojIyMjIyPP+bS1sUQIV2s95pBDDvmbP/mdkft83tpYguZq5Jh/OeaYh+yzy8hTHvNlaxNNczm+la9OTlar1UdA/+C2A4trRCnD3jS8BB1obq2Gk6GU6QbQAS4BUaYSQAf4bhhKKTFdAzrAOwAxEUAH+KEM01SY3gM6wBsEAQB0gJ+maZoC3gI6iPYaAIBJsiRmHU0AALOeFC3aK2cWAACUXe7+AwO0lc9eTHYTAAAAAElFTkSuQmCC)
|
||||
no-repeat;
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
.tvu span:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media screen and (-webkit-min-device-pixel-ratio: 0) {
|
||||
.tvu {
|
||||
-webkit-animation: webkit-adjacent-element-selector-bugfix infinite 1s;
|
||||
}
|
||||
@-webkit-keyframes webkit-adjacent-element-selector-bugfix {
|
||||
from {
|
||||
padding: 0;
|
||||
}
|
||||
to {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#uploader {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 28px;
|
||||
line-height: 24px;
|
||||
padding-left: 10px;
|
||||
background-color: #444;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
#tree {
|
||||
position: absolute;
|
||||
top: 28px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 160px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
#editor,
|
||||
#preview {
|
||||
position: absolute;
|
||||
top: 28px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 160px;
|
||||
border-left: 1px solid #eee;
|
||||
}
|
||||
|
||||
#preview {
|
||||
background-color: #eee;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#loader {
|
||||
position: absolute;
|
||||
top: 36%;
|
||||
right: 40%;
|
||||
}
|
||||
|
||||
.loader {
|
||||
z-index: 10000;
|
||||
border: 8px solid #b5b5b5;
|
||||
border-top: 8px solid #3498db;
|
||||
border-bottom: 8px solid #3498db;
|
||||
border-radius: 50%;
|
||||
width: 240px;
|
||||
height: 240px;
|
||||
animation: spin 2s linear infinite;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function ge(e) {
|
||||
return document.getElementById(e)
|
||||
}
|
||||
|
||||
function ce(e) {
|
||||
return document.createElement(e)
|
||||
}
|
||||
|
||||
function sortByKey(e, t) {
|
||||
return e.sort(function(e, n) {
|
||||
var a = e[t],
|
||||
i = n[t];
|
||||
return i > a ? -1 : a > i ? 1 : 0
|
||||
})
|
||||
}
|
||||
|
||||
function createFileUploader(e, t, n) {
|
||||
var isIpad = /(iPhone)*(OS ([7-9]|1[0-1])_)/i.test(navigator.userAgent);
|
||||
|
||||
function a(e, n) {
|
||||
200 != e ? alert("ERROR[" + e + "]: " + n) : t.refreshPath(d.value)
|
||||
}
|
||||
|
||||
function i(e) {
|
||||
var t = new FormData;
|
||||
t.append("path", e), requests.add("PUT", "/edit", t, a)
|
||||
}
|
||||
var o = ce("button");
|
||||
o.innerHTML = "Root Dir", ge(e).appendChild(o);
|
||||
var c = ce("input");
|
||||
c.type = "file", c.multiple = !1, c.name = "data", c.id = "upload-select", ge(e).appendChild(c);
|
||||
var d = ce("input");
|
||||
d.id = "upload-path", d.type = "text", d.name = "path", d.defaultValue = "/", ge(e).appendChild(d);
|
||||
var s = ce("button");
|
||||
s.innerHTML = "Upload", ge(e).appendChild(s);
|
||||
var r = ce("button");
|
||||
r.innerHTML = "Create", ge(e).appendChild(r);
|
||||
var l = ce("input");
|
||||
l.id = "editor-filename", l.type = "text", l.disabled = !0, l.size = 20, ge(e).appendChild(l);
|
||||
var y = ce("input");
|
||||
y.id = "ipad-fix", y.name = "ipad-fix", y.type = "checkbox", isIpad ? y.checked = true : y.checked = false;
|
||||
var z = ce("label");
|
||||
z.for = y.id, z.innerHTML = " Alt.";
|
||||
var u = ce("button");
|
||||
u.innerHTML = " Save ", ge(e).appendChild(u), ge(e).appendChild(z), ge(e).appendChild(y), r.onclick = function(e) {
|
||||
i(d.value), n.loadUrl(d.value), d.value = "/"
|
||||
}, u.onclick = function(e) {
|
||||
if (y.checked) {
|
||||
var edi = ace.edit("editor");
|
||||
var towrite = edi.getValue();
|
||||
var q = new FormData;
|
||||
q.append("rawname", l.value);
|
||||
var xi = 0;
|
||||
const chunkSize = 4096;
|
||||
for (var start = 0; start < towrite.length; start += chunkSize) {
|
||||
var chunk = towrite.substring(start, start + chunkSize);
|
||||
q.append("raw" + xi, chunk);
|
||||
xi++;
|
||||
}
|
||||
requests.add("POST", "/edit", q, a);
|
||||
} else {
|
||||
n.execCommand("saveCommand");
|
||||
}
|
||||
}, o.onclick = function(e) {
|
||||
t.refreshPath(d.value)
|
||||
}, s.onclick = function(e) {
|
||||
if (0 !== c.files.length) {
|
||||
var t = new FormData;
|
||||
t.append("data", c.files[0], d.value), requests.add("POST", "/edit", t, a);
|
||||
var n = ge("upload-path");
|
||||
n.value = "/";
|
||||
var i = ge("upload-select");
|
||||
i.value = ""
|
||||
}
|
||||
}, c.onchange = function(e){
|
||||
if(c.files.length === 0) return;
|
||||
var fnm = c.files[0].name;
|
||||
var ext = /(?:\.([^.]+))?$/.exec(fnm)[1];
|
||||
var name = /(.*)\.[^.]+$/.exec(fnm)[1];
|
||||
if(typeof name !== undefined){
|
||||
fnm = name;
|
||||
}
|
||||
d.value = "/"+fnm+"."+ext;
|
||||
};
|
||||
}
|
||||
|
||||
function createTree(e, t) {
|
||||
|
||||
function n(e) {
|
||||
ge("download-frame").src = "/edit?download=" + e
|
||||
}
|
||||
|
||||
function a(e) {
|
||||
var t = ge("editor-filename");
|
||||
t.value = e, ge("editor").style.display = "none", h.style.display = "block", h.innerHTML = '<img src="/edit?edit=' + e + "&_cb=" + Date.now() + '" style="max-width:100%; max-height:100%; margin:auto; display:block;" />'
|
||||
}
|
||||
|
||||
function i(e, i) {
|
||||
var o = ce("ul");
|
||||
e.appendChild(o);
|
||||
var c = ce("li");
|
||||
o.appendChild(c), r(i) ? (c.innerHTML = "<span>Preview</span>", c.onclick = function(t) {
|
||||
a(i), document.body.getElementsByClassName("cm").length > 0 && document.body.removeChild(e)
|
||||
}) : s(i) && (c.innerHTML = "<span>Edit</span>", c.onclick = function(n) {
|
||||
t.loadUrl(i), document.body.getElementsByClassName("cm").length > 0 && document.body.removeChild(e)
|
||||
});
|
||||
var d = ce("li");
|
||||
o.appendChild(d), s(i) || r(i) || k(i) ? (d.innerHTML = "<span>Download</span>", d.onclick = function(t) {
|
||||
n(i), document.body.getElementsByClassName("cm").length > 0 && document.body.removeChild(e)
|
||||
}) : z(i) ? (o.appendChild(d), d.innerHTML = "<span>ChDir</span>", d.onclick = function(t) {
|
||||
f.removeChild(f.childNodes[0]), m(f, i), document.body.getElementsByClassName("cm").length > 0 && document.body.removeChild(e)
|
||||
}) : ();
|
||||
var l = ce("li");
|
||||
o.appendChild(l), l.innerHTML = "<span>Delete</span>", l.onclick = function(t) {
|
||||
u(i), document.body.getElementsByClassName("cm").length > 0 && document.body.removeChild(e)
|
||||
}
|
||||
}
|
||||
|
||||
function o(e, t, n) {
|
||||
var a = ce("div"),
|
||||
o = document.body.scrollTop ? document.body.scrollTop : document.documentElement.scrollTop,
|
||||
c = document.body.scrollLeft ? document.body.scrollLeft : document.documentElement.scrollLeft,
|
||||
d = e.clientX + c,
|
||||
s = e.clientY + o;
|
||||
a.className = "cm", a.style.display = "block", a.style.left = d + "px", a.style.top = s + "px", i(a, t), document.body.appendChild(a);
|
||||
var r = a.offsetWidth,
|
||||
l = a.offsetHeight;
|
||||
a.onmouseout = function(e) {
|
||||
(e.clientX < d || e.clientX > d + r || e.clientY < s || e.clientY > s + l) && document.body.getElementsByClassName("cm").length > 0 && document.body.removeChild(a)
|
||||
}
|
||||
}
|
||||
|
||||
function c(e, n, i) {
|
||||
var c = ce("li");
|
||||
c.id = n;
|
||||
var d = ce("span");
|
||||
return d.innerHTML = n, c.appendChild(d), c.onclick = function(e) {
|
||||
s(c.id.toLowerCase()) ? t.loadUrl(c.id) : r(c.id.toLowerCase()) ? a(c.id) : z(c.id) ? f.removeChild(f.childNodes[0]) && m(f, c.id.toLowerCase()) : ()
|
||||
}, c.oncontextmenu = function(e) {
|
||||
e.preventDefault(), e.stopPropagation(), o(e, c.id, !0)
|
||||
}, c
|
||||
}
|
||||
|
||||
function d(e, t, n) {
|
||||
sortByKey(n, "name");
|
||||
var a = ce("ul");
|
||||
e.appendChild(a);
|
||||
for (var i = n.length, o = 0; i > o; o++) "file" === n[o].type && a.appendChild(c(t, n[o].name, n[o].size))
|
||||
}
|
||||
|
||||
function z(e) {
|
||||
if (e.indexOf('.') == -1) return !0
|
||||
else return !1
|
||||
}
|
||||
|
||||
function s(e) {
|
||||
var t = /(?:.([^.]+))?$/.exec(e)[1];
|
||||
if (void 0 !== typeof t) switch (t) {
|
||||
case "txt":
|
||||
case "htm":
|
||||
case "html":
|
||||
case "js":
|
||||
case "css":
|
||||
case "xml":
|
||||
case "json":
|
||||
case "conf":
|
||||
case "ini":
|
||||
case "h":
|
||||
case "c":
|
||||
case "cpp":
|
||||
case "php":
|
||||
case "hex":
|
||||
case "ino":
|
||||
case "pde":
|
||||
return !0
|
||||
}
|
||||
return !1
|
||||
}
|
||||
|
||||
function r(e) {
|
||||
var t = /(?:.([^.]+))?$/.exec(e)[1];
|
||||
if (void 0 !== typeof t) switch (t) {
|
||||
case "png":
|
||||
case "jpg":
|
||||
case "gif":
|
||||
case "bmp":
|
||||
return !0
|
||||
}
|
||||
return !1
|
||||
}
|
||||
|
||||
function k(e) { // other types may be there and good for download
|
||||
var t = /(?:.([^.]+))?$/.exec(e)[1];
|
||||
if (void 0 !== typeof t) switch (t) {
|
||||
case "ico":
|
||||
case "gz":
|
||||
case "zip":
|
||||
case "wav":
|
||||
case "mp3":
|
||||
case "pdf":
|
||||
return !0
|
||||
}
|
||||
return !1
|
||||
}
|
||||
|
||||
function l(e) {
|
||||
return function(e, t) {
|
||||
200 != e ? alert("ERROR[" + e + "]: " + t) : (f.removeChild(f.childNodes[0]), m(f, "/"))
|
||||
}
|
||||
}
|
||||
|
||||
function u(e) {
|
||||
var t = new FormData;
|
||||
t.append("path", e), requests.add("DELETE", "/edit", t, l(e))
|
||||
}
|
||||
|
||||
function p(e, t) {
|
||||
return function(n, a) {
|
||||
200 == n && d(e, t, JSON.parse(a))
|
||||
}
|
||||
}
|
||||
|
||||
function m(e, t) {
|
||||
requests.add("GET", "/edit", {
|
||||
list: t
|
||||
}, p(e, t))
|
||||
}
|
||||
var h = ge("preview"),
|
||||
f = ce("div");
|
||||
return f.className = "tvu", ge(e).appendChild(f), this.refreshPath = function(e) {
|
||||
f.removeChild(f.childNodes[0]), m(f, "/")
|
||||
}, m(f, "/"), this
|
||||
}
|
||||
|
||||
function createEditor(e, t, n, a, i) {
|
||||
function o(e) {
|
||||
var t = "plain",
|
||||
n = /(?:.([^.]+))?$/.exec(e)[1];
|
||||
if (void 0 !== typeof n) switch (n) {
|
||||
case "txt":
|
||||
t = "plain";
|
||||
break;
|
||||
case "hex":
|
||||
t = "plain";
|
||||
break;
|
||||
case "conf":
|
||||
t = "plain";
|
||||
break;
|
||||
case "htm":
|
||||
t = "html";
|
||||
break;
|
||||
case "js":
|
||||
t = "javascript";
|
||||
break;
|
||||
case "h":
|
||||
t = "c_cpp";
|
||||
break;
|
||||
case "c":
|
||||
t = "c_cpp";
|
||||
break;
|
||||
case "cpp":
|
||||
t = "c_cpp";
|
||||
break;
|
||||
case "css":
|
||||
case "scss":
|
||||
case "php":
|
||||
case "html":
|
||||
case "json":
|
||||
case "xml":
|
||||
case "ini":
|
||||
t = n
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
function c(e, t) {
|
||||
200 != e && alert("ERROR[" + e + "]: " + t)
|
||||
}
|
||||
|
||||
function d(e, t, n) {
|
||||
var a = new FormData;
|
||||
a.append("data", new Blob([t], {
|
||||
type: n
|
||||
}), e), requests.add("POST", "/edit", a, c)
|
||||
}
|
||||
|
||||
function s(e, t) {
|
||||
ge("preview").style.display = "none", ge("editor").style.display = "block", 200 == e ? l.setValue(t) : l.setValue(""), l.clearSelection()
|
||||
}
|
||||
|
||||
function r(e) {
|
||||
requests.add("GET", "/edit", {
|
||||
edit: e
|
||||
}, s)
|
||||
}
|
||||
"undefined" == typeof t && (t = "/index.htm"), "undefined" == typeof n && (n = o(t)), "undefined" == typeof a && (a = "monokai"), "undefined" == typeof i && (i = "text/" + n, "c_cpp" === n && (i = "text/plain"));
|
||||
var l = ace.edit(e);
|
||||
return "plain" !== n && l.getSession().setMode("ace/mode/" + n), l.setTheme("ace/theme/" + a), l.$blockScrolling = 1 / 0, l.getSession().setUseSoftTabs(!0), l.getSession().setTabSize(2), l.getSession().setUseWorker(!0), l.setHighlightActiveLine(!0), l.setShowPrintMargin(!1), l.commands.addCommand({
|
||||
name: "saveCommand",
|
||||
bindKey: {
|
||||
win: "Ctrl-S",
|
||||
mac: "Command-S"
|
||||
},
|
||||
exec: function(e) {
|
||||
d(t, e.getValue() + "", i)
|
||||
},
|
||||
readOnly: !1
|
||||
}), l.commands.addCommand({
|
||||
name: "undoCommand",
|
||||
bindKey: {
|
||||
win: "Ctrl-Z",
|
||||
mac: "Command-Z"
|
||||
},
|
||||
exec: function(e) {
|
||||
e.getSession().getUndoManager().undo(!1)
|
||||
},
|
||||
readOnly: !1
|
||||
}), l.commands.addCommand({
|
||||
name: "redoCommand",
|
||||
bindKey: {
|
||||
win: "Ctrl-Shift-Z",
|
||||
mac: "Command-Shift-Z"
|
||||
},
|
||||
exec: function(e) {
|
||||
e.getSession().getUndoManager().redo(!1)
|
||||
},
|
||||
readOnly: !1
|
||||
}), l.loadUrl = function(e) {
|
||||
var a = ge("editor-filename");
|
||||
a.value = e, t = e, n = o(t), i = "text/" + n, "plain" !== n && l.getSession().setMode("ace/mode/" + n), r(t)
|
||||
}, l
|
||||
}
|
||||
|
||||
function onBodyLoad() {
|
||||
var e = {},
|
||||
t = (window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(t, n, a) {
|
||||
e[n] = a
|
||||
}), createEditor("editor", e.file, e.lang, e.theme)),
|
||||
n = createTree("tree", t);
|
||||
|
||||
window.define = ace.define;
|
||||
window.require = ace.require;
|
||||
ace.config.set('basePath', '/');
|
||||
ace.config.set("workerPath", '/');
|
||||
|
||||
createFileUploader("uploader", n, t), "undefined" == typeof e.file && (e.file = "/index.htm"), t.loadUrl(e.file)
|
||||
}
|
||||
"undefined" == typeof XMLHttpRequest && (XMLHttpRequest = function() {
|
||||
try {
|
||||
return new ActiveXObject("Msxml2.XMLHTTP.6.0")
|
||||
} catch (e) {}
|
||||
try {
|
||||
return new ActiveXObject("Msxml2.XMLHTTP.3.0")
|
||||
} catch (e) {}
|
||||
try {
|
||||
return new ActiveXObject("Microsoft.XMLHTTP")
|
||||
} catch (e) {}
|
||||
throw new Error("This browser does not support XMLHttpRequest.")
|
||||
});
|
||||
var QueuedRequester = function() {
|
||||
this.queue = [], this.running = !1, this.xmlhttp = null
|
||||
};
|
||||
QueuedRequester.prototype = {
|
||||
_request: function(e) {
|
||||
function t(e, t) {
|
||||
return function() {
|
||||
4 == e.readyState && (ge("loader").style.display = "none", t.callback(e.status, e.responseText), 0 === n.queue.length && (n.running = !1), n.running && n._request(n.queue.shift()))
|
||||
}
|
||||
}
|
||||
if (this.running = !0, !(!e instanceof Object)) {
|
||||
var n = this;
|
||||
ge("loader").style.display = "block";
|
||||
var a = "";
|
||||
if (e.params instanceof FormData) a = e.params;
|
||||
else if (e.params instanceof Object)
|
||||
for (var i in e.params) a += "" === a ? "GET" === e.method ? "?" : "" : "&", a += encodeURIComponent(i) + "=" + encodeURIComponent(e.params[i]);
|
||||
this.xmlhttp = new XMLHttpRequest, this.xmlhttp.onreadystatechange = t(this.xmlhttp, e), "GET" === e.method ? (this.xmlhttp.open(e.method, e.url + a, !0), this.xmlhttp.send()) : (this.xmlhttp.open(e.method, e.url, !0), a instanceof String && this.xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"), this.xmlhttp.send(a))
|
||||
}
|
||||
},
|
||||
stop: function() {
|
||||
this.running && (this.running = !1), this.xmlhttp && this.xmlhttp.readyState < 4 && this.xmlhttp.abort()
|
||||
},
|
||||
add: function(e, t, n, a) {
|
||||
this.queue.push({
|
||||
url: t,
|
||||
method: e,
|
||||
params: n,
|
||||
callback: a
|
||||
}), this.running || this._request(this.queue.shift())
|
||||
}
|
||||
};
|
||||
var requests = new QueuedRequester;
|
||||
</script>
|
||||
<script id="ace" src="/acefull.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script>
|
||||
if ("undefined" == typeof ace.edit) {
|
||||
var script = document.createElement("script");
|
||||
(script.src = "/ace.js"), (script.async = !1), document.head.appendChild(script);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body onload="onBodyLoad()">
|
||||
<div id="loader" class="loader"></div>
|
||||
<div id="uploader"></div>
|
||||
<div id="tree"></div>
|
||||
<div id="editor"></div>
|
||||
<div id="preview" style="display: none;"></div>
|
||||
<iframe id="download-frame" style="display: none;"></iframe>
|
||||
</body>
|
||||
</html>
|
231
src/edit.htm.gz.h
Normal file
231
src/edit.htm.gz.h
Normal file
@ -0,0 +1,231 @@
|
||||
|
||||
//File: edit.htm.gz, Size: 4503
|
||||
#define edit_htm_gz_len 4503
|
||||
const uint8_t edit_htm_gz[] PROGMEM = {
|
||||
0x1F,0x8B,0x08,0x08,0x5A,0xE6,0xAE,0x5F,0x02,0x00,0x65,0x64,0x69,0x74,0x2E,0x68,0x74,0x6D,0x00,0xB5,
|
||||
0x1A,0x0B,0x5B,0xDB,0x36,0xF0,0xAF,0x18,0x6F,0x63,0xF6,0xE2,0x38,0x0E,0x50,0xD6,0x3A,0x18,0x16,0x1E,
|
||||
0xEB,0xBB,0x50,0x12,0xDA,0xD1,0x8E,0xED,0x53,0x6C,0x25,0x56,0xB1,0x25,0xCF,0x96,0x09,0x34,0xCD,0x7F,
|
||||
0xDF,0x49,0xF2,0x93,0x84,0xEE,0xF1,0x6D,0xA5,0x60,0x49,0xA7,0x3B,0xDD,0x9D,0xEE,0x25,0xD9,0x7B,0x1B,
|
||||
0xC7,0xA7,0x47,0xE3,0xCB,0xB3,0x13,0x2D,0xE4,0x71,0xB4,0xBF,0x27,0xFE,0x6A,0x11,0xA2,0x33,0x4F,0xC7,
|
||||
0x54,0xDF,0xDF,0x8B,0x31,0x47,0x00,0xE2,0x49,0x17,0xFF,0x91,0x93,0x1B,0x4F,0x3F,0x62,0x94,0x63,0xCA,
|
||||
0xBB,0xFC,0x2E,0xC1,0xBA,0xE6,0xAB,0x9E,0xA7,0x73,0x7C,0xCB,0x7B,0x02,0x79,0xA0,0xF9,0x21,0x4A,0x33,
|
||||
0xCC,0xBD,0x9C,0x4F,0xBB,0x8F,0x81,0x04,0x27,0x3C,0xC2,0xFB,0x27,0xA3,0x33,0xED,0x24,0x20,0x9C,0xA5,
|
||||
0x7B,0x3D,0x35,0xB2,0x17,0x11,0x7A,0xAD,0xA5,0x38,0xF2,0x74,0x94,0x24,0x11,0xEE,0x72,0x96,0xFB,0x61,
|
||||
0x97,0x00,0x4D,0x5D,0x0B,0x53,0x3C,0xF5,0xF4,0x1E,0xF2,0xB1,0x0D,0x03,0xBA,0x26,0x96,0xF3,0x74,0x12,
|
||||
0xA3,0x19,0xEE,0xDD,0xAA,0x39,0x4D,0x02,0x59,0xC8,0x52,0xEE,0xE7,0x5C,0xFB,0x57,0xD8,0xFF,0x00,0x29,
|
||||
0xE3,0x77,0x11,0x56,0x20,0x25,0xB3,0x9F,0x65,0xBA,0x16,0xE3,0x80,0x20,0xE0,0xC2,0x4F,0xB1,0xD0,0x5A,
|
||||
0x84,0x26,0x38,0x5A,0x4C,0x19,0xA8,0x29,0x23,0x9F,0xB1,0xDB,0xDF,0x4A,0x6E,0x07,0xB2,0x3B,0x45,0x31,
|
||||
0x89,0xEE,0xDC,0x0C,0xD1,0xAC,0x9B,0xE1,0x94,0x4C,0x97,0xB6,0x1F,0x2F,0x3E,0x77,0x09,0x0D,0xF0,0xAD,
|
||||
0xBB,0xED,0x38,0x83,0x84,0x65,0x84,0x13,0x46,0x5D,0x34,0xC9,0x58,0x94,0x73,0x3C,0x88,0xF0,0x94,0xBB,
|
||||
0x8F,0x80,0xC2,0x84,0xA5,0x01,0x4E,0xDD,0x7E,0x72,0xAB,0x01,0x88,0x04,0xDA,0x37,0x3B,0x3B,0x3B,0x83,
|
||||
0x09,0xF2,0xAF,0x67,0x29,0xCB,0x69,0xD0,0xF5,0x59,0xC4,0x52,0xF7,0x9B,0xE9,0x23,0xF1,0x33,0x08,0x48,
|
||||
0x96,0x44,0xE8,0xCE,0xA5,0x8C,0x62,0xC0,0xBD,0xED,0x66,0x21,0x0A,0xD8,0xDC,0x75,0x34,0x47,0xEB,0x3B,
|
||||
0x40,0x24,0x9D,0x4D,0x90,0xE1,0x58,0xE2,0xC7,0xDE,0x31,0x07,0x7F,0x8B,0x5F,0x35,0x3C,0xC7,0x64,0x16,
|
||||
0x72,0xF7,0x47,0xC7,0x11,0xFC,0x6B,0x79,0xB4,0x88,0x48,0xC6,0xBB,0x52,0x39,0x6A,0x3D,0xCE,0x12,0xD7,
|
||||
0x91,0xAC,0xC3,0x23,0x46,0xE9,0x8C,0x50,0x68,0x24,0x28,0x08,0x08,0x9D,0xB9,0x0A,0x2D,0x22,0x8B,0x4A,
|
||||
0x5A,0xD8,0x07,0xC4,0xC9,0x0D,0x1E,0xC4,0x84,0x76,0xE7,0x24,0xE0,0xA1,0xBB,0xEB,0x00,0x17,0x7E,0x9E,
|
||||
0x66,0x20,0x53,0xC2,0x08,0xE5,0x38,0x95,0x68,0x59,0x82,0xE8,0x42,0x89,0x2A,0x14,0x50,0xC9,0x49,0x28,
|
||||
0xEC,0x28,0xEE,0x4E,0x22,0xE6,0x5F,0x57,0x2B,0xED,0x26,0xB7,0xC5,0x5A,0x6E,0xC8,0x6E,0x70,0xBA,0xA8,
|
||||
0xD5,0x25,0xB1,0x5B,0xC0,0x16,0x69,0x8C,0xF1,0xD2,0xE6,0x37,0x39,0x40,0x2D,0xF9,0x04,0x29,0x0B,0xAA,
|
||||
0x4D,0x91,0xDA,0x82,0x17,0x28,0x84,0x26,0x39,0x5F,0xAC,0x6E,0x25,0x4B,0x90,0x4F,0xF8,0x9D,0xEB,0xC8,
|
||||
0x69,0xD2,0x42,0xDC,0x1D,0xC7,0xD1,0x84,0xC2,0xB5,0x77,0x38,0x0D,0x10,0x45,0xD6,0x30,0x25,0x28,0xB2,
|
||||
0x46,0xB5,0xCA,0xBB,0x31,0xFB,0xDC,0xCD,0xA1,0x0D,0xFD,0x08,0xFB,0x5C,0x69,0x18,0x36,0x61,0x72,0x4D,
|
||||
0xF8,0x2A,0x60,0x65,0xA0,0xA1,0x2B,0xA9,0xA1,0x50,0xED,0x5E,0x1F,0x74,0x23,0xF9,0x50,0x62,0x2B,0x89,
|
||||
0xBA,0x13,0xC6,0x39,0x8B,0xA5,0xBD,0x55,0xD2,0x6A,0xD2,0x64,0x1E,0xAF,0x6C,0xC7,0x7A,0xD5,0x37,0xC8,
|
||||
0x0F,0x40,0xA9,0x9C,0xF8,0x28,0xEA,0xA2,0x88,0xCC,0xA8,0x1B,0x93,0x20,0x88,0x70,0xC3,0x64,0xDD,0x3C,
|
||||
0x8D,0x8C,0x00,0x71,0xE4,0x2A,0x3F,0x4B,0xE8,0x0C,0xA0,0x19,0xDE,0xDD,0xB1,0xC8,0xBB,0xC3,0xD3,0xF3,
|
||||
0xB9,0xF3,0xF2,0xE9,0x8C,0x0D,0xE1,0xDF,0x9B,0xD1,0x45,0x78,0x72,0x31,0x83,0xD6,0xA1,0xE8,0x0E,0xDF,
|
||||
0x1E,0x0D,0xDF,0xC2,0xE3,0xA8,0xDF,0xF9,0x34,0xFD,0x63,0x28,0x86,0x9F,0x06,0x87,0xE3,0x8B,0x93,0xE1,
|
||||
0xF0,0x65,0xEF,0xF9,0x9B,0xF9,0xFB,0x97,0xBB,0x02,0x7C,0x18,0x39,0xE7,0xEF,0x42,0xE7,0x62,0xEB,0x49,
|
||||
0x1C,0x3C,0x0B,0x42,0x3F,0xBE,0x18,0xBE,0x7D,0x7F,0x7E,0x73,0x19,0x5F,0xCC,0x46,0xEF,0xFB,0xE1,0x87,
|
||||
0xAD,0x77,0xA3,0x0F,0xEF,0x7F,0xBE,0xC6,0xBF,0x3C,0x7B,0xF1,0x61,0x3C,0x07,0x84,0x63,0x36,0xBA,0x38,
|
||||
0x3F,0x7C,0x77,0x38,0xFB,0x70,0xE8,0x9F,0xDC,0x4E,0xA2,0x37,0x87,0x4F,0x87,0x93,0xE1,0x96,0x8F,0xF1,
|
||||
0x6C,0x7C,0x7E,0x98,0x3F,0x7F,0x79,0x3A,0x23,0x24,0x1C,0x7D,0x78,0x33,0xF6,0x8F,0x1E,0xBD,0xBA,0x78,
|
||||
0x36,0x24,0xE1,0x9B,0x17,0xE7,0xCE,0xF5,0xD3,0x97,0x47,0xC7,0xFE,0xE5,0x8B,0xCB,0xDD,0xE3,0xED,0xDE,
|
||||
0x8F,0x3F,0xBE,0x0E,0x4E,0x09,0x1D,0xDF,0x7C,0x1E,0xCE,0x8E,0xE6,0x8F,0xEF,0xB2,0x71,0xF8,0xFC,0x86,
|
||||
0xF6,0xDE,0xB1,0x4F,0xCF,0xEF,0x5E,0xC3,0xEF,0xD9,0x59,0x67,0x32,0xEA,0x67,0x17,0x6F,0x9F,0xBF,0xDB,
|
||||
0xCA,0x9E,0x3C,0x4A,0x0E,0x8F,0x8F,0x6F,0xE2,0xC9,0x59,0x2F,0x0E,0xAE,0xA7,0xFC,0xF1,0x36,0x4F,0x2E,
|
||||
0x67,0xF9,0x87,0x3F,0x1E,0xBD,0x08,0x7B,0xA7,0x18,0x5D,0x86,0x9D,0xBB,0xCF,0x77,0x8F,0xC3,0xF1,0xB3,
|
||||
0x9B,0x37,0x11,0xBA,0x7D,0xF3,0xC6,0xFF,0x1C,0x77,0x22,0xF4,0xE4,0x74,0x1C,0xA1,0xB4,0x7F,0x11,0x0C,
|
||||
0x7B,0x9D,0xA3,0xAD,0xE1,0x0E,0x4F,0xCF,0x8F,0xE8,0xF1,0xF6,0xA7,0xD1,0xE3,0xC3,0xC3,0x3E,0x9B,0xFC,
|
||||
0xB1,0xF5,0xF4,0x7A,0xF7,0xE9,0xC5,0xEE,0xDB,0xC9,0xDB,0xE1,0x68,0xE7,0xF0,0x02,0x5D,0x8E,0xDE,0x0E,
|
||||
0xA7,0x3B,0x93,0x30,0x7C,0xF9,0x72,0xFC,0x73,0x30,0xFC,0x9C,0x0E,0x4F,0xE7,0xC3,0xDB,0x93,0x8B,0xE1,
|
||||
0xB3,0xCE,0xCB,0x93,0xD7,0x4E,0x7F,0x74,0xB9,0x3D,0x7B,0xBD,0x3B,0x3F,0xCC,0x4E,0x86,0x6F,0x0F,0x9D,
|
||||
0xD9,0x8B,0x4E,0x8C,0x3E,0xB0,0xA3,0xED,0xD9,0xF3,0x5D,0x72,0x76,0x89,0x86,0xCF,0x0F,0x5F,0x64,0xE4,
|
||||
0x3C,0x7E,0x76,0xE1,0x0C,0x87,0xAF,0x4E,0xF1,0xCF,0x47,0xDB,0xE8,0xE5,0x96,0xFF,0x1E,0xF4,0x7F,0xF1,
|
||||
0x0B,0xFE,0xB1,0x33,0x9C,0x9F,0x3A,0x91,0xFF,0x04,0x8F,0x9F,0x5D,0x8E,0xE5,0xEE,0x9C,0x44,0x3F,0x8F,
|
||||
0xAF,0x47,0xF9,0xDB,0xF8,0xE8,0xC8,0xD4,0x28,0xEB,0xA6,0x38,0xC1,0x88,0x37,0x63,0x55,0xE5,0x1C,0x60,
|
||||
0x5B,0xB5,0x15,0x16,0x4E,0x2A,0x22,0x6B,0x37,0xC0,0x3E,0x4B,0x91,0x9C,0x03,0x08,0x38,0x15,0x26,0xB6,
|
||||
0xFC,0x49,0x86,0x5A,0x4D,0x45,0x5A,0x0D,0xD1,0x40,0x33,0x4A,0x47,0x88,0x09,0x05,0x9C,0x1B,0xE2,0xE3,
|
||||
0x6E,0x42,0x6E,0x71,0xD4,0x95,0xC8,0xAE,0x63,0x2E,0xA4,0xB3,0x95,0xD3,0x10,0x25,0xB1,0xA2,0x5A,0x0E,
|
||||
0x04,0x9F,0x90,0x2F,0x52,0x1A,0xB8,0x4C,0x0C,0xCF,0xC2,0x77,0x58,0xDA,0x9D,0xE4,0xB3,0x29,0xB9,0x05,
|
||||
0x6F,0x9E,0x12,0x4A,0x38,0xD6,0xFA,0xD9,0xF2,0xA7,0x92,0xCC,0x35,0xBE,0x9B,0xA6,0x28,0xC6,0x99,0xF6,
|
||||
0x37,0xC9,0x2C,0xA6,0x29,0x8B,0x17,0x75,0x04,0xE4,0xAC,0xD1,0x59,0x2E,0xBF,0xC9,0x93,0x88,0x21,0x10,
|
||||
0x73,0x25,0x6C,0x94,0xC1,0x34,0x15,0x6E,0x55,0x05,0xD5,0xD2,0xCD,0xB6,0xC0,0x2D,0x5B,0x6E,0xBD,0xB5,
|
||||
0x53,0xBB,0x6E,0x57,0xCE,0x15,0xC1,0xBE,0xA9,0xFA,0x46,0x3C,0x68,0xC4,0xBA,0x6F,0x38,0xA8,0x74,0xFD,
|
||||
0xE2,0x6A,0x11,0x15,0x19,0x6A,0x06,0x54,0x94,0xEE,0xEF,0x3A,0x8D,0x50,0x01,0xF3,0x96,0xDF,0x60,0x99,
|
||||
0xE6,0xAD,0x6F,0x92,0x14,0x76,0x03,0xCF,0xBF,0x42,0xB3,0x92,0xA9,0x4D,0x1B,0x68,0x56,0xE9,0x4E,0x89,
|
||||
0x50,0xE7,0x3C,0xC9,0x6B,0x45,0x7A,0x55,0x2A,0x00,0x57,0xDC,0x3C,0x12,0xDC,0x7C,0x55,0xAB,0xDB,0xBB,
|
||||
0xDF,0x15,0x3C,0xEC,0x38,0xDF,0x2D,0x6D,0x35,0xB7,0xCA,0xC7,0x7D,0x07,0xFE,0x95,0x69,0xF7,0x71,0xCD,
|
||||
0xC2,0xE4,0x91,0xF8,0x29,0x19,0x04,0x3A,0x4D,0xE0,0xF6,0xCE,0x93,0xC7,0xC1,0xA4,0x04,0x2A,0xC1,0x1E,
|
||||
0x86,0xA7,0x28,0x20,0x79,0xE6,0x3E,0x72,0xBE,0x53,0xFA,0x84,0xED,0x03,0xD9,0xCB,0xCD,0x55,0x9D,0xDA,
|
||||
0x62,0xB3,0x84,0x50,0x6D,0x2B,0xD3,0xC4,0x7E,0xA3,0xB4,0x32,0xCC,0x66,0xAA,0x07,0x17,0xA9,0x4D,0x53,
|
||||
0xCC,0x5F,0x38,0xDF,0x2D,0x78,0x8A,0x68,0x36,0x65,0x69,0xEC,0xA6,0x8C,0x23,0x8E,0x0D,0xC7,0x5C,0xF6,
|
||||
0x9D,0x75,0x80,0xED,0x5D,0x27,0xC0,0x33,0x73,0xB9,0xDC,0xEB,0xC9,0xA4,0x06,0x15,0x8F,0x9F,0x92,0x84,
|
||||
0xEF,0x4F,0x73,0xEA,0x0B,0x1E,0xB4,0x19,0x36,0xB0,0xB9,0x48,0x31,0xCF,0x53,0xAA,0x05,0xCC,0xCF,0x85,
|
||||
0xB1,0xDB,0x33,0xCC,0x4F,0x94,0xDD,0x1F,0xDE,0x3D,0x0F,0x60,0xC6,0xB2,0x42,0xF0,0xD7,0x22,0x80,0x03,
|
||||
0xC3,0x7A,0x05,0x4E,0x6B,0x3E,0x24,0x18,0x20,0xF2,0x12,0xDF,0x19,0xD8,0xE2,0x15,0x22,0xB6,0xC5,0xB8,
|
||||
0x61,0x94,0xD3,0x00,0x48,0xCD,0xC5,0x0D,0x68,0x01,0x79,0xF8,0x23,0xBF,0xB2,0x88,0x47,0xE1,0x31,0x28,
|
||||
0xA6,0x93,0x7D,0x74,0xD0,0xED,0xBB,0x68,0x9F,0x1C,0xF4,0xC1,0xBF,0xCC,0x06,0x7D,0xB5,0xF2,0xCF,0x24,
|
||||
0xC2,0x17,0x85,0xCB,0x89,0x85,0x6A,0x6A,0x3D,0x83,0x9C,0x85,0xA0,0x48,0xF3,0x07,0xE3,0x74,0xA4,0x19,
|
||||
0x1F,0x7F,0xEC,0x3E,0xB9,0xFA,0xD2,0xFF,0xE8,0x74,0xFB,0x57,0xE6,0xEF,0x66,0x8F,0xD8,0x1C,0x67,0xDC,
|
||||
0xA0,0xE8,0x86,0xCC,0x10,0xD8,0xB9,0x2D,0xF2,0xED,0x70,0x06,0x52,0x98,0x83,0x6A,0x0D,0xA2,0xD8,0xDB,
|
||||
0x72,0x9C,0x0D,0x0F,0x1F,0xA0,0x08,0x03,0xEB,0xFA,0xC9,0xF9,0xF9,0xE9,0xF9,0x47,0xBD,0x83,0x3B,0xFA,
|
||||
0x95,0xAB,0xE9,0x1D,0x6A,0xBA,0xDC,0x86,0xA2,0x33,0xC5,0x59,0x78,0x86,0x78,0x68,0x04,0xF6,0x0D,0x8A,
|
||||
0x72,0xD0,0x85,0x60,0x84,0x79,0xA0,0x38,0x7D,0x92,0x73,0xCE,0xA8,0x6E,0x0E,0x98,0x4D,0x28,0xC5,0xE9,
|
||||
0xB3,0xF1,0xEB,0x57,0x9E,0x7E,0xCE,0x18,0xD7,0x8E,0x49,0xAA,0x5B,0x72,0x37,0x6C,0x94,0x24,0x98,0x06,
|
||||
0x47,0x21,0x89,0x02,0x83,0x99,0x03,0x81,0xEE,0x4B,0x74,0x59,0x88,0x00,0xB6,0x6F,0xAB,0x92,0x75,0x0A,
|
||||
0x52,0xEB,0x96,0x6F,0xC7,0x79,0xC4,0x49,0x12,0x61,0x6F,0xA3,0x0F,0x3D,0x8A,0x62,0x80,0x89,0x34,0x2C,
|
||||
0x60,0x24,0xF0,0x74,0x15,0x8C,0x8A,0x10,0xB6,0x6E,0x15,0x5F,0xAD,0x12,0xB4,0x56,0x09,0x9A,0xB8,0x09,
|
||||
0x48,0xA4,0x5B,0x81,0x5D,0xD7,0xCA,0xD0,0x2B,0x96,0x2A,0x61,0x01,0x9E,0x22,0xE0,0xE4,0x9D,0x90,0x1A,
|
||||
0x4A,0xEF,0x75,0x0B,0x05,0x6A,0xA1,0xAC,0xAD,0x8D,0xAC,0xA9,0x0D,0xB5,0x8D,0xEB,0x90,0x33,0x85,0x9C,
|
||||
0xB6,0x91,0xD3,0x26,0xF2,0x91,0xB4,0x86,0x75,0xC8,0xA9,0x42,0x8E,0x5A,0x22,0x46,0x52,0x44,0x15,0xE1,
|
||||
0xBA,0x42,0x9B,0x20,0x10,0x60,0x47,0x4D,0x31,0xA1,0x07,0x0E,0x89,0x26,0x11,0x0E,0xBC,0x0D,0x07,0x7A,
|
||||
0xA2,0xA8,0xF6,0xB6,0x9C,0x35,0x6B,0x44,0x6A,0x8D,0xBC,0xB5,0x46,0x2E,0xD7,0x20,0x09,0x28,0x11,0xF2,
|
||||
0x86,0x6E,0xE5,0x52,0x6B,0xED,0x11,0xB5,0x9C,0x1F,0x62,0xFF,0x7A,0xC2,0xE4,0x88,0x6C,0x8B,0x15,0x37,
|
||||
0x90,0xA4,0x99,0x48,0x9A,0xF2,0x28,0x02,0x34,0x13,0x1B,0xBC,0xDC,0x13,0x94,0xAD,0xA4,0x29,0xBE,0x36,
|
||||
0x8C,0xB8,0xAD,0x4B,0x84,0xB8,0xAD,0xA5,0xB8,0x35,0x6D,0x84,0x6E,0xB0,0xB6,0x4E,0x4B,0xB1,0xB9,0x66,
|
||||
0x30,0x59,0x37,0x98,0x9B,0x56,0x6A,0x33,0xEA,0x47,0xC4,0xBF,0xF6,0x6A,0x37,0x36,0x17,0x46,0xB3,0x23,
|
||||
0x38,0xE1,0x1E,0xC5,0x73,0xED,0x67,0x08,0x4B,0xC7,0x88,0xA3,0x01,0x2F,0xC8,0x18,0x85,0xDD,0x60,0x20,
|
||||
0x04,0xA7,0x51,0xF0,0xC2,0xCC,0x46,0x01,0x0C,0x9F,0x5D,0x8C,0x75,0x4B,0xEF,0x89,0x5D,0xD1,0xC1,0x93,
|
||||
0x89,0xB9,0x34,0x2B,0x5F,0xB2,0xA8,0x8C,0xEA,0x17,0x69,0x54,0x0F,0x41,0xA3,0xB4,0xB8,0xA5,0x15,0xAF,
|
||||
0xE5,0x89,0x4C,0x8D,0x4A,0xA5,0x25,0x53,0xE2,0x64,0x28,0xD6,0x30,0x8A,0xFD,0xD7,0x4D,0x08,0x7B,0xCA,
|
||||
0x78,0x0D,0xD3,0x42,0x6D,0xA6,0x51,0xC5,0x74,0x8A,0xE6,0xA5,0x91,0xA8,0xF5,0x07,0xCA,0xBD,0x1D,0xC8,
|
||||
0xBE,0x34,0xE3,0x1A,0xF6,0x76,0x9C,0x27,0xBB,0x03,0xD8,0x1F,0x43,0x39,0x2E,0x00,0xF6,0xB8,0x0D,0xA6,
|
||||
0x35,0xE3,0xE1,0xC0,0xEF,0x78,0x85,0x56,0x02,0x8F,0xDB,0x59,0x3E,0xC9,0x78,0x0A,0xB9,0xCD,0xF0,0x2D,
|
||||
0xBF,0x83,0xCD,0xF6,0x32,0x7A,0x87,0x59,0x81,0x69,0xB1,0x4E,0x67,0x79,0x4F,0x41,0xA7,0xA3,0x86,0x86,
|
||||
0x90,0xD0,0x10,0x8E,0x32,0xAC,0x51,0x1B,0xDF,0x62,0xFF,0x88,0xC5,0x31,0x12,0x24,0x32,0xD8,0xE4,0xA2,
|
||||
0xA3,0x9B,0x4B,0x8B,0xAD,0xD5,0xCC,0x03,0x21,0xCB,0xCA,0x1E,0xD2,0x23,0x84,0x40,0xCF,0xB7,0x85,0xAF,
|
||||
0x64,0x85,0x50,0x5F,0xDF,0xE5,0x32,0x10,0x29,0x94,0x8F,0xCE,0x95,0x55,0x6D,0xDC,0x8A,0x54,0xED,0x7D,
|
||||
0x97,0x56,0xD7,0x8A,0x3F,0x66,0xBD,0xD3,0x02,0xD6,0x8E,0x6B,0x35,0x54,0x5F,0x2E,0x2D,0x5F,0xF0,0x1F,
|
||||
0x22,0x3A,0xC3,0x7F,0x5F,0x80,0x9A,0x47,0xE9,0xA2,0x16,0x85,0xE4,0x71,0xE0,0xFE,0x6A,0x1B,0x1F,0x7F,
|
||||
0xB3,0xAF,0x3A,0xA6,0x79,0xF0,0x6D,0x4F,0x6A,0xD8,0xE0,0xE6,0xC7,0xFE,0x95,0x85,0x00,0x6C,0xFF,0x60,
|
||||
0xFE,0x6A,0x4B,0x70,0x0B,0x36,0xB8,0x61,0x24,0xD0,0xC4,0x4A,0xC2,0xB3,0xD9,0x54,0x43,0x9B,0x9B,0x06,
|
||||
0x98,0x5C,0xCB,0x58,0x3B,0xBC,0xA3,0xDB,0x90,0x37,0x96,0xCB,0xFB,0xD9,0x6C,0x9C,0x62,0xAC,0xD2,0x65,
|
||||
0x05,0x90,0xDC,0x83,0xCC,0x2B,0xC1,0xAA,0x94,0x1A,0x5B,0x35,0x14,0x06,0x65,0xBA,0xB7,0x8B,0x4A,0xC2,
|
||||
0xD3,0x29,0xA3,0x60,0xB2,0xC9,0xFD,0x61,0x79,0xC4,0xD3,0x5B,0x31,0xE4,0xFB,0x3D,0x12,0xCF,0xB4,0x2C,
|
||||
0xF5,0x3D,0xB5,0x15,0x07,0xE2,0x8F,0xF7,0xBD,0x48,0x73,0x9B,0xBF,0xFB,0x13,0x4F,0xEF,0xC0,0xFE,0x62,
|
||||
0x9B,0xB2,0xB9,0x61,0x76,0xBE,0xD7,0x35,0x49,0xD1,0xD3,0x63,0x74,0x5B,0x1C,0xF4,0x45,0x25,0x32,0xD0,
|
||||
0xA0,0x5F,0x96,0xB0,0x6A,0xA0,0x38,0x62,0xA3,0x9C,0xB3,0x81,0x56,0x56,0x38,0xEA,0x88,0xA9,0x6B,0xBD,
|
||||
0xFD,0xEF,0x6B,0x1D,0x20,0x10,0x1D,0xA9,0x3D,0x21,0x32,0x88,0xE5,0x22,0xE4,0xE1,0x56,0xF4,0x21,0x85,
|
||||
0xDB,0xA9,0xA8,0x48,0x00,0x4E,0xEE,0xA5,0x4D,0x2B,0x33,0x90,0x79,0x60,0xB4,0x12,0xED,0x9E,0x38,0x94,
|
||||
0xEC,0x9F,0xA9,0x6A,0x73,0xAF,0x27,0x7B,0xFA,0x3A,0xC7,0x00,0xCD,0x53,0x40,0xB7,0xAA,0xFA,0x66,0xC2,
|
||||
0x82,0xBB,0x46,0x55,0x94,0x1D,0xDE,0x1D,0x45,0x28,0xCB,0xDE,0xA0,0x18,0xD6,0xF7,0x63,0xDD,0x2C,0x4C,
|
||||
0x69,0xDF,0xD9,0xDC,0x6C,0x23,0xA5,0x38,0x66,0x37,0x58,0x71,0x85,0x21,0x94,0xB9,0x01,0x10,0xDE,0xDC,
|
||||
0x5C,0xC7,0x98,0xB8,0x50,0xFB,0x1A,0x57,0x14,0xDC,0xB5,0x0C,0x7F,0xFF,0x13,0x77,0x2A,0x47,0x3E,0xA4,
|
||||
0xD5,0xC8,0xB4,0x04,0xF7,0x5F,0xBE,0x64,0xF2,0xEF,0x4A,0xA0,0x97,0xFE,0xB2,0xEA,0x2E,0x58,0xBA,0x04,
|
||||
0xB8,0xDE,0x7D,0xAF,0xE0,0x66,0x36,0x27,0xDC,0x0F,0x85,0xBE,0x7D,0x94,0x61,0x5D,0xDC,0xD4,0xB9,0xB2,
|
||||
0x35,0xFB,0x5C,0x34,0x3E,0x93,0xA4,0x68,0xCD,0xD1,0x4D,0xD1,0x8A,0x93,0xED,0xA2,0x95,0x04,0x53,0xDD,
|
||||
0x55,0x45,0xE2,0x86,0xB3,0x2C,0x1A,0xFD,0xA5,0xDC,0xFA,0x68,0x55,0xC3,0xC7,0x6C,0x4E,0x85,0x02,0x2B,
|
||||
0x2D,0x47,0x6B,0xF6,0xBE,0x4E,0x61,0x95,0xD3,0x05,0x05,0x5E,0x77,0x9A,0x2A,0x9F,0x6B,0xF8,0x47,0x09,
|
||||
0x03,0xCF,0xC0,0x4B,0xF3,0xFF,0xB2,0x1A,0x5F,0x59,0xCD,0xCA,0x7E,0xAC,0x11,0xF2,0x28,0x84,0x5A,0xF2,
|
||||
0x2F,0x24,0x8C,0x5B,0x2B,0xC4,0xB6,0x2F,0x9E,0x6F,0x58,0x20,0xA3,0x9F,0x69,0xE5,0x46,0x6C,0xFD,0x8F,
|
||||
0x06,0x96,0xB4,0x0C,0xAC,0x5D,0x69,0x24,0x6B,0x76,0x0D,0x47,0x98,0xE3,0x4A,0xA2,0x64,0xAD,0x44,0xE9,
|
||||
0xFF,0xA3,0xF9,0x3A,0x2C,0x11,0x75,0xAE,0x68,0x06,0xA6,0x80,0xDC,0xE8,0x90,0xA0,0xBD,0x36,0x85,0xCC,
|
||||
0x4F,0x59,0x14,0x8D,0x59,0x72,0xF0,0xC0,0xB8,0x5B,0x8D,0x17,0x8D,0x92,0xCD,0x7A,0x8A,0xE5,0xAF,0x25,
|
||||
0xFA,0x0A,0x4F,0xF9,0xC1,0x43,0x80,0x87,0xC9,0xD6,0x73,0xAC,0xC0,0xC3,0x36,0x28,0x0F,0x46,0x7F,0xE9,
|
||||
0xF8,0x56,0x56,0xF5,0x2E,0x3B,0x0C,0xF6,0xC2,0x2F,0x95,0xE4,0x09,0x25,0x59,0x64,0x6D,0xBE,0xA8,0xC7,
|
||||
0x23,0xA0,0xE8,0x05,0x1D,0x3D,0xB9,0xAD,0xC7,0x38,0x4B,0xBC,0x4C,0x0D,0x21,0x83,0x58,0xFC,0xDE,0xA6,
|
||||
0xAC,0x8B,0xE2,0xA9,0x47,0x6C,0x36,0x9D,0x66,0x98,0xBF,0x17,0x19,0xC4,0x8A,0xAA,0xFE,0x33,0x99,0x41,
|
||||
0x06,0xD0,0xA5,0x31,0xCB,0x33,0xCC,0x72,0xDE,0xAE,0x35,0x2B,0x61,0xF6,0x82,0x2F,0x5F,0xAA,0xCE,0x7E,
|
||||
0xD0,0x49,0xEB,0xEE,0xE5,0x5E,0xD6,0xE8,0xEC,0x67,0x9D,0x08,0x3C,0xE9,0x3F,0xB6,0x14,0xD2,0xB4,0x14,
|
||||
0x26,0x12,0x98,0xC5,0x94,0xA5,0xA4,0xB5,0xAD,0xA7,0xE2,0x24,0x80,0x1A,0x21,0x56,0x18,0xB4,0x6E,0x96,
|
||||
0x87,0xDC,0xA6,0x2B,0x23,0x2B,0x6D,0x7B,0xFA,0x43,0xD5,0x76,0x60,0x08,0xAA,0xA0,0xF5,0x57,0x6C,0x8E,
|
||||
0xD3,0x23,0x94,0x61,0x03,0x22,0x6F,0x95,0x26,0x24,0xD4,0x74,0xB3,0xB5,0xB3,0x68,0x01,0xF5,0xD5,0x13,
|
||||
0xC2,0xCB,0x57,0xC3,0x02,0x4C,0x10,0x71,0x61,0x0D,0x25,0x73,0xA9,0xB8,0x63,0x54,0x9C,0x98,0x40,0x43,
|
||||
0x79,0x8B,0x47,0x6C,0x8B,0x0B,0x1E,0xD0,0xDB,0xB1,0x3A,0x20,0x42,0x79,0x8D,0xC1,0x5A,0x58,0x72,0x96,
|
||||
0xB2,0x04,0xCD,0x90,0x98,0x07,0x63,0xE0,0x60,0x92,0xB8,0xA0,0xD6,0xA8,0x88,0xEA,0xEB,0x86,0x6E,0xDF,
|
||||
0x03,0x7B,0x95,0x97,0x39,0xA7,0x53,0x03,0x6A,0x27,0xB3,0x9E,0x16,0xFC,0x77,0x49,0x88,0xDF,0xF2,0x22,
|
||||
0xBB,0x84,0x3C,0xAE,0x5B,0x51,0xD1,0xFC,0x94,0x15,0x0D,0x3F,0x2B,0x5B,0xB7,0x0D,0x20,0xA3,0x25,0x98,
|
||||
0xD1,0x69,0xD1,0x24,0x94,0x94,0x74,0x4A,0x60,0xF9,0x4C,0x92,0x32,0x93,0x85,0x65,0x2B,0xC4,0xB7,0x15,
|
||||
0x1E,0xAB,0xF2,0x1C,0x5E,0x97,0xE7,0xEA,0x5B,0x96,0xFF,0x4E,0xFC,0x84,0xCE,0x4A,0x61,0x92,0xB2,0x35,
|
||||
0x23,0xA5,0x28,0x93,0x38,0xF9,0x2A,0x23,0xE9,0xBF,0x3D,0xF5,0x1D,0x9F,0xBC,0x3A,0x19,0x9F,0x34,0x0F,
|
||||
0x00,0xCD,0xCB,0x21,0xFE,0x57,0xB7,0x2F,0xDC,0x74,0x8D,0xBF,0x91,0xD4,0xA0,0xEC,0x36,0xCD,0xD6,0xFD,
|
||||
0x51,0xD4,0xBA,0x97,0xAA,0x33,0x4A,0x71,0xE1,0x03,0x7A,0xDA,0xDC,0x6C,0x30,0x22,0xC7,0xEB,0x3B,0x2D,
|
||||
0x6A,0xE9,0xAA,0x0A,0x97,0x5E,0x8D,0x1E,0x2A,0x57,0x91,0x59,0x1D,0x08,0x89,0x47,0x8B,0x88,0x62,0x89,
|
||||
0xB3,0x21,0xD9,0xF7,0xE1,0x50,0xD8,0x31,0xD5,0x5D,0x8E,0xE7,0xC1,0xB5,0x97,0x7F,0x25,0xEF,0x03,0x36,
|
||||
0x37,0x51,0xBB,0xA4,0x35,0x1C,0x4B,0x02,0xC5,0x82,0xB2,0x25,0x6F,0x22,0x40,0x16,0x60,0xCB,0xB1,0x5E,
|
||||
0x8C,0x4E,0xDF,0xD8,0x09,0x4A,0xC1,0x21,0xA9,0xD9,0x8C,0x46,0xB9,0x12,0xB0,0xAD,0xED,0xA7,0x27,0x8D,
|
||||
0xB3,0x96,0x7C,0xDD,0xE7,0xF2,0xA5,0x15,0x19,0x82,0x9C,0xCA,0xD0,0xA2,0xEC,0x29,0xEE,0x65,0x21,0xC7,
|
||||
0xC5,0x65,0xBE,0xAB,0xC3,0x55,0xDC,0xCC,0x16,0xFC,0x26,0x7F,0xE0,0x3A,0x81,0x87,0x24,0x6B,0x9E,0x31,
|
||||
0x5B,0x91,0xE1,0x6F,0xEE,0xD8,0xB2,0x6C,0x29,0x72,0xF7,0x8F,0x4B,0xEA,0x45,0xB4,0xDA,0x1C,0x79,0x1C,
|
||||
0x5E,0xD4,0xA1,0xB8,0x36,0x47,0x1D,0x52,0x18,0xA1,0xBA,0x45,0xFF,0xA9,0x87,0xD0,0xD2,0x43,0xE8,0x6A,
|
||||
0x80,0xC0,0xB7,0x2D,0x6F,0xAF,0x56,0x19,0x4C,0x80,0xB3,0xEB,0x41,0x1D,0x45,0x00,0x02,0xCF,0xA8,0x05,
|
||||
0xF8,0x94,0xC9,0xF1,0x4F,0xE8,0x06,0xA9,0x6B,0xD9,0x1A,0xBA,0x3E,0x54,0xC8,0xE9,0xFE,0xEF,0xD0,0xAA,
|
||||
0x66,0xB6,0x03,0x51,0x06,0xCD,0xD5,0x78,0xC2,0x5B,0xE1,0xA9,0x8E,0x59,0xCD,0xE8,0x04,0xEE,0x5A,0x78,
|
||||
0xB3,0xC6,0x1B,0xFA,0x6D,0xB8,0x1E,0x18,0xE4,0x43,0xBE,0xD7,0x8C,0xC3,0x0A,0xA3,0x69,0x3D,0xEB,0x0F,
|
||||
0xA3,0xED,0xE3,0xEA,0xFA,0x3A,0x43,0xFA,0x1F,0x3E,0xC8,0xEC,0xAC,0xBC,0x98,0x01,0x37,0x6F,0xF4,0x74,
|
||||
0x30,0x88,0x0C,0xCC,0x10,0xA3,0x74,0x24,0xEE,0x00,0x54,0x22,0x59,0xAA,0x0D,0xF4,0xA4,0xEB,0x1A,0xA0,
|
||||
0xB1,0x9E,0x4C,0x19,0x36,0xE8,0x01,0xE6,0x57,0x40,0x0A,0x40,0xEA,0x31,0x20,0xD9,0x18,0x44,0x30,0x88,
|
||||
0x3C,0x3D,0x66,0x94,0x5D,0x23,0xD2,0x9C,0x4E,0x00,0x42,0x8A,0xAF,0x04,0xF4,0x0E,0xB5,0x8A,0x7D,0x90,
|
||||
0x84,0x6A,0x88,0xDA,0x7D,0xB3,0xB8,0xE5,0xAC,0xEF,0x98,0x70,0xE9,0x36,0x85,0x7D,0x6C,0x48,0xB4,0x4C,
|
||||
0x14,0x1E,0x23,0x9C,0x65,0x92,0x6F,0x21,0xD7,0x6B,0x16,0x80,0x58,0x80,0xD6,0x8B,0xA1,0x25,0x16,0x02,
|
||||
0x09,0x05,0x60,0x1C,0xE2,0xB8,0x80,0x70,0x68,0x0A,0x10,0x12,0xA0,0x6F,0xA5,0xAA,0x46,0xB2,0xC0,0x23,
|
||||
0x74,0xE6,0xF5,0x7B,0x8E,0xB5,0x4A,0xF6,0x22,0xC3,0x23,0x36,0xE5,0x63,0x34,0xC9,0x8C,0x0D,0xC7,0x5C,
|
||||
0x9D,0x21,0x40,0x23,0x88,0x26,0xC6,0x96,0xB9,0x1E,0xFD,0x3D,0x4B,0xAF,0x71,0x5A,0x20,0x8B,0xCA,0x0C,
|
||||
0x0A,0xB3,0x08,0x7E,0xF9,0xD0,0x17,0x2F,0xFA,0x5F,0x11,0x8A,0x6B,0xE0,0x28,0x64,0xF3,0xB3,0x94,0x50,
|
||||
0xFE,0x5A,0x1E,0xFA,0x8D,0x8D,0xBE,0xDC,0x27,0x75,0x17,0x25,0x23,0x50,0x79,0x49,0xB5,0x10,0xC1,0xCC,
|
||||
0x6D,0x5D,0x55,0x59,0x13,0xD8,0x2E,0x88,0xAD,0xEE,0x62,0x4E,0xA8,0xAB,0x1F,0xF1,0x34,0xEA,0x8E,0x74,
|
||||
0x2B,0x46,0x3E,0x74,0xD4,0x1C,0xE8,0x2F,0x2D,0xE1,0xB4,0x6E,0x33,0x98,0x6C,0xD4,0x9D,0xBA,0x50,0x5F,
|
||||
0xB9,0xC7,0x6B,0x5F,0x4B,0x09,0xE0,0x61,0xC4,0x26,0x86,0x78,0xE7,0xB0,0x10,0x0E,0xEF,0xD2,0xA5,0x29,
|
||||
0xB2,0xD3,0xD7,0xAF,0xDC,0x7C,0x88,0xBA,0xDC,0xC2,0x8D,0x1B,0xC3,0x8E,0xAE,0x8B,0x8B,0x38,0xC0,0x43,
|
||||
0xC1,0x29,0x8D,0xEE,0x5C,0xC8,0x8A,0x7F,0x21,0x75,0x4E,0x03,0xF6,0x35,0xA9,0x3F,0xDC,0x93,0xFA,0xC3,
|
||||
0x3A,0xA9,0x71,0x7B,0xB7,0xA0,0x73,0x01,0x64,0x5F,0x23,0x8A,0x66,0x38,0x85,0x01,0xB1,0x08,0x6C,0xC0,
|
||||
0x3F,0xE3,0x2C,0xC5,0x5F,0xE7,0x6C,0x14,0x92,0x29,0x5F,0xE1,0x4F,0x8E,0xFE,0x3B,0x2E,0x53,0xFC,0x00,
|
||||
0x97,0x65,0x21,0x5B,0xA5,0x8D,0xBF,0xBE,0x07,0x93,0x4E,0xED,0xC1,0x16,0x36,0xDD,0xF5,0x5F,0xF8,0x5D,
|
||||
0xBD,0xE2,0xD7,0xB3,0xA7,0x78,0xB8,0x78,0x69,0x05,0xC2,0x26,0x40,0x82,0x46,0x7E,0x62,0xF4,0x10,0x4E,
|
||||
0x0A,0xAF,0x40,0x04,0x43,0x99,0x23,0xF6,0x16,0x4B,0x8B,0x7B,0xC6,0x9C,0x80,0xF8,0x73,0x10,0xCE,0x97,
|
||||
0xD5,0xAF,0x2D,0xBE,0x56,0x02,0x25,0x00,0x93,0x90,0x67,0x7B,0x1F,0x0F,0x36,0xAF,0x3A,0x90,0xA0,0x3C,
|
||||
0x78,0x98,0x1E,0x34,0x36,0xAF,0x7E,0x30,0x7B,0x33,0xD2,0x28,0x89,0x64,0xAA,0x03,0xBD,0x7E,0xA4,0x57,
|
||||
0x1E,0x5A,0x42,0x08,0x6B,0xA6,0xC2,0x2A,0xB0,0x5A,0x58,0x5E,0x69,0xC2,0x43,0x7C,0x04,0x06,0x0F,0x19,
|
||||
0x3B,0x60,0x36,0xF5,0x1A,0x37,0x8D,0x3A,0x87,0xBF,0x3A,0x84,0xED,0x41,0xC1,0x56,0x80,0xA7,0x84,0x62,
|
||||
0x19,0xBD,0x54,0xD3,0x2A,0x00,0x42,0x0D,0x24,0x95,0x90,0xB2,0x6D,0x89,0xB6,0xC8,0x7E,0x64,0x26,0x14,
|
||||
0x69,0xE8,0xE2,0x83,0x0F,0x91,0xE8,0x75,0x99,0xB0,0x57,0xC0,0x73,0x19,0x48,0x1A,0x13,0xD6,0xBC,0xC0,
|
||||
0xD3,0xF3,0xA2,0xA5,0x83,0x94,0xBC,0x11,0x84,0x95,0x38,0x10,0x6F,0x55,0xE3,0x5E,0x68,0xAF,0x4F,0x3D,
|
||||
0x0A,0x6C,0x2E,0xC1,0xC5,0x94,0x00,0x81,0x5E,0xA5,0xF3,0x5F,0x5E,0xBF,0x7A,0xC6,0x79,0x72,0xAE,0x76,
|
||||
0x14,0x68,0xB5,0x07,0x6A,0x43,0x33,0x17,0x3C,0xBD,0x2B,0x0B,0x43,0x11,0x28,0x54,0xA8,0xFB,0xE5,0x74,
|
||||
0xF2,0x09,0xFB,0x20,0xC9,0xEB,0x0C,0x72,0xE8,0x96,0x2D,0xD0,0xC7,0xE3,0x33,0x7B,0xD7,0x76,0xA0,0x52,
|
||||
0x81,0xFD,0xF4,0x43,0x61,0x31,0xCB,0x7F,0x84,0xBC,0xFD,0xCF,0x90,0x09,0xC4,0xFB,0x0C,0x02,0x7A,0x89,
|
||||
0xDF,0xC6,0x0D,0x53,0x36,0x97,0x68,0x27,0x69,0x2A,0x8C,0x61,0x1C,0x92,0x4C,0x9B,0xC0,0x60,0x86,0x53,
|
||||
0x2D,0x60,0x38,0xD3,0x28,0xE3,0x5A,0x96,0x27,0x09,0x4B,0xF9,0x3D,0x7D,0xD8,0x7A,0x79,0x49,0xF3,0x36,
|
||||
0xC7,0x39,0x0E,0x8A,0x61,0x9C,0x36,0xF5,0x22,0x6B,0x3A,0x18,0x07,0x77,0xFB,0x78,0x55,0x54,0x78,0x39,
|
||||
0xA5,0x22,0xFF,0x6C,0xF4,0x55,0x1F,0xA4,0x0B,0x81,0xAA,0x47,0xF3,0x28,0x5A,0x0E,0xEE,0xD1,0xB2,0x93,
|
||||
0x94,0x71,0x26,0x5F,0x7A,0x2D,0x7E,0x2F,0x5C,0xCB,0xBD,0x77,0x4D,0xDF,0xA6,0xEA,0x58,0x1B,0xC6,0x06,
|
||||
0xD6,0x08,0xCD,0x38,0xA2,0xBE,0xD8,0x46,0xA5,0x0B,0xB3,0xAC,0xEE,0xC4,0xF4,0x81,0x88,0x0B,0xD2,0x70,
|
||||
0x1E,0x2C,0x29,0xA4,0x64,0xD4,0xD3,0x75,0x51,0xE8,0x61,0x51,0x2F,0xA3,0x38,0x6B,0x90,0xAD,0x32,0x85,
|
||||
0x49,0xBD,0x12,0x3C,0x90,0x2F,0x57,0x9A,0xF3,0x57,0xD9,0x28,0xAB,0x7B,0x04,0x30,0xAD,0x9C,0x67,0xD2,
|
||||
0x8E,0xA7,0xCB,0x0A,0xE1,0x40,0x86,0x0C,0x69,0xC0,0x31,0xE6,0x21,0x0B,0x0E,0xF4,0x03,0xDD,0xD5,0xE1,
|
||||
0xFF,0xA6,0x6E,0xC1,0x2C,0x4C,0x7D,0x16,0xE0,0x8B,0xF3,0xE7,0x10,0x4A,0x13,0x46,0x31,0xE5,0x06,0x82,
|
||||
0x8C,0xE2,0xE9,0x9D,0x55,0x48,0xC5,0xC6,0x47,0x74,0x65,0x0E,0xDA,0xCA,0xC6,0xF3,0x7B,0xDB,0xD9,0xDA,
|
||||
0x0C,0x9B,0x51,0x11,0x5D,0xEF,0x80,0x79,0x8E,0x57,0x5E,0x8E,0x58,0x74,0xF5,0x14,0x64,0x2E,0x76,0x04,
|
||||
0xCF,0x12,0x6B,0x04,0x58,0xC2,0xF5,0x6A,0x25,0x3F,0x54,0xD9,0x51,0xDB,0x47,0x51,0x24,0x3E,0xE6,0x00,
|
||||
0x5E,0xC5,0x62,0x79,0x66,0x09,0x22,0x19,0x08,0x90,0xE1,0x31,0xBE,0x05,0x97,0x96,0xE5,0x99,0xB2,0xA1,
|
||||
0xE2,0x38,0x04,0xA4,0x79,0xC3,0x8C,0x4C,0xAB,0xEA,0x6D,0x6E,0x72,0xBB,0x34,0x13,0xA3,0x44,0xCA,0x44,
|
||||
0xB2,0x91,0x77,0x0F,0x4B,0xA3,0x29,0xA4,0xC8,0xDD,0x2B,0xDA,0x36,0xDA,0x6A,0x48,0x30,0x35,0x4A,0x18,
|
||||
0x70,0x96,0xA7,0x11,0x64,0x08,0x51,0xBD,0xB4,0xA6,0x65,0x98,0x06,0xB0,0x80,0xFB,0x97,0xC8,0x12,0x95,
|
||||
0x36,0xAD,0x62,0xC4,0x53,0xC5,0x77,0x9B,0x1E,0x2F,0x76,0xE5,0x19,0x96,0x61,0xAE,0xFD,0xF5,0xAD,0x25,
|
||||
0xBF,0x9B,0x25,0x2A,0x21,0xF4,0xE0,0x6D,0xCA,0x7C,0xDE,0x05,0xB3,0x8A,0xBB,0xB0,0x82,0xB2,0x82,0x40,
|
||||
0x5F,0xC3,0xA1,0x3A,0xED,0x59,0xE2,0x3A,0xC5,0xBD,0xE7,0xA2,0xB5,0xFE,0x8C,0x7B,0x3E,0xDA,0xA6,0xD3,
|
||||
0xE6,0xB3,0xB1,0xDB,0x7B,0x3B,0xF7,0x40,0x68,0xC2,0x52,0x6E,0x40,0x8E,0x83,0x34,0x58,0xAD,0x56,0x1E,
|
||||
0xBC,0x9A,0x71,0xC1,0x4E,0xF2,0x2C,0x34,0x16,0xC0,0xBA,0xCB,0x2D,0xA5,0x2B,0x17,0x5B,0xCA,0x6E,0x5D,
|
||||
0x6A,0x95,0xF6,0xE1,0x42,0xEA,0x6A,0xC5,0x8F,0x2F,0x5F,0x64,0xAF,0xDE,0x6D,0xE8,0xB5,0x37,0x1C,0x84,
|
||||
0x95,0x4E,0x5C,0x66,0x64,0x61,0xF4,0xF7,0x63,0xD5,0x5E,0x4F,0x9D,0xA7,0xCA,0xCF,0x5D,0x34,0x12,0x78,
|
||||
0x22,0xB9,0xEB,0xC5,0x7B,0x2D,0x68,0x4E,0x21,0x2E,0xD9,0x70,0x00,0xAB,0xBE,0x6F,0xD6,0x8B,0x0F,0x9C,
|
||||
0xEF,0xE3,0xEE,0x57,0x27,0x42,0xCF,0xAB,0xEA,0x79,0x15,0x75,0xD4,0x04,0xEF,0x81,0x8F,0x60,0x74,0x05,
|
||||
0x86,0x03,0xB3,0x6A,0xD8,0xD5,0xDA,0x62,0x5D,0xAB,0x18,0x44,0xD9,0x1D,0xF5,0x45,0xD4,0xAC,0xA8,0x84,
|
||||
0xA0,0xFE,0xD6,0x21,0x5A,0xCD,0x34,0x97,0x35,0x67,0xE2,0xFA,0x51,0x63,0xC5,0x5B,0x87,0x66,0x8D,0x01,
|
||||
0xEC,0xC3,0x19,0x5D,0x88,0x5B,0xBA,0xA8,0x26,0x0F,0xE9,0x55,0x17,0xC4,0x83,0x09,0xF5,0xAC,0x3C,0x59,
|
||||
0x0F,0x90,0x25,0xC1,0xCA,0xA0,0xAA,0x2A,0x56,0x86,0x8B,0x83,0x5E,0xF5,0x2E,0xB0,0xF1,0xFD,0x52,0x4D,
|
||||
0x83,0xC8,0x57,0x27,0x72,0x7E,0xFB,0x6D,0xCA,0x83,0x68,0x0A,0x63,0xFF,0x4F,0x51,0x09,0xC4,0x79,0xD4,
|
||||
0x2E,0x00,0x00
|
||||
};
|
Reference in New Issue
Block a user