8 | #endif
9 | #include "ESPAsyncWebSrv.h"
10 |
11 | DNSServer dnsServer;
12 | AsyncWebServer server(80);
13 |
14 | class CaptiveRequestHandler : public AsyncWebHandler {
15 | public:
16 | CaptiveRequestHandler() {}
17 | virtual ~CaptiveRequestHandler() {}
18 |
19 | bool canHandle(AsyncWebServerRequest *request){
20 | //request->addInterestingHeader("ANY");
21 | return true;
22 | }
23 |
24 | void handleRequest(AsyncWebServerRequest *request) {
25 | AsyncResponseStream *response = request->beginResponseStream("text/html");
26 | response->print("Captive Portal ");
27 | response->print("This is out captive portal front page.
");
28 | response->printf("You were trying to reach: http://%s%s
", request->host().c_str(), request->url().c_str());
29 | response->printf("Try opening this link instead
", WiFi.softAPIP().toString().c_str());
30 | response->print("");
31 | request->send(response);
32 | }
33 | };
34 |
35 |
36 | void setup(){
37 | //your other setup stuff...
38 | WiFi.softAP("esp-captive");
39 | dnsServer.start(53, "*", WiFi.softAPIP());
40 | server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER);//only when requested from AP
41 | //more handlers...
42 | server.begin();
43 | }
44 |
45 | void loop(){
46 | dnsServer.processNextRequest();
47 | }
48 |
--------------------------------------------------------------------------------
/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino:
--------------------------------------------------------------------------------
1 | #include
2 | #ifdef ESP32
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #elif defined(ESP8266)
9 | #include
10 | #include
11 | #include
12 | #endif
13 | #include
14 | #include
15 |
16 | // SKETCH BEGIN
17 | AsyncWebServer server(80);
18 | AsyncWebSocket ws("/ws");
19 | AsyncEventSource events("/events");
20 |
21 | void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){
22 | if(type == WS_EVT_CONNECT){
23 | Serial.printf("ws[%s][%u] connect\n", server->url(), client->id());
24 | client->printf("Hello Client %u :)", client->id());
25 | client->ping();
26 | } else if(type == WS_EVT_DISCONNECT){
27 | Serial.printf("ws[%s][%u] disconnect\n", server->url(), client->id());
28 | } else if(type == WS_EVT_ERROR){
29 | Serial.printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data);
30 | } else if(type == WS_EVT_PONG){
31 | Serial.printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len)?(char*)data:"");
32 | } else if(type == WS_EVT_DATA){
33 | AwsFrameInfo * info = (AwsFrameInfo*)arg;
34 | String msg = "";
35 | if(info->final && info->index == 0 && info->len == len){
36 | //the whole message is in a single frame and we got all of it's data
37 | Serial.printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT)?"text":"binary", info->len);
38 |
39 | if(info->opcode == WS_TEXT){
40 | for(size_t i=0; i < info->len; i++) {
41 | msg += (char) data[i];
42 | }
43 | } else {
44 | char buff[3];
45 | for(size_t i=0; i < info->len; i++) {
46 | sprintf(buff, "%02x ", (uint8_t) data[i]);
47 | msg += buff ;
48 | }
49 | }
50 | Serial.printf("%s\n",msg.c_str());
51 |
52 | if(info->opcode == WS_TEXT)
53 | client->text("I got your text message");
54 | else
55 | client->binary("I got your binary message");
56 | } else {
57 | //message is comprised of multiple frames or the frame is split into multiple packets
58 | if(info->index == 0){
59 | if(info->num == 0)
60 | Serial.printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary");
61 | Serial.printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len);
62 | }
63 |
64 | Serial.printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT)?"text":"binary", info->index, info->index + len);
65 |
66 | if(info->opcode == WS_TEXT){
67 | for(size_t i=0; i < len; i++) {
68 | msg += (char) data[i];
69 | }
70 | } else {
71 | char buff[3];
72 | for(size_t i=0; i < len; i++) {
73 | sprintf(buff, "%02x ", (uint8_t) data[i]);
74 | msg += buff ;
75 | }
76 | }
77 | Serial.printf("%s\n",msg.c_str());
78 |
79 | if((info->index + len) == info->len){
80 | Serial.printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len);
81 | if(info->final){
82 | Serial.printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary");
83 | if(info->message_opcode == WS_TEXT)
84 | client->text("I got your text message");
85 | else
86 | client->binary("I got your binary message");
87 | }
88 | }
89 | }
90 | }
91 | }
92 |
93 |
94 | const char* ssid = "*******";
95 | const char* password = "*******";
96 | const char * hostName = "esp-async";
97 | const char* http_username = "admin";
98 | const char* http_password = "admin";
99 |
100 | void setup(){
101 | Serial.begin(115200);
102 | Serial.setDebugOutput(true);
103 | WiFi.mode(WIFI_AP_STA);
104 | WiFi.softAP(hostName);
105 | WiFi.begin(ssid, password);
106 | if (WiFi.waitForConnectResult() != WL_CONNECTED) {
107 | Serial.printf("STA: Failed!\n");
108 | WiFi.disconnect(false);
109 | delay(1000);
110 | WiFi.begin(ssid, password);
111 | }
112 |
113 | //Send OTA events to the browser
114 | ArduinoOTA.onStart([]() { events.send("Update Start", "ota"); });
115 | ArduinoOTA.onEnd([]() { events.send("Update End", "ota"); });
116 | ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
117 | char p[32];
118 | sprintf(p, "Progress: %u%%\n", (progress/(total/100)));
119 | events.send(p, "ota");
120 | });
121 | ArduinoOTA.onError([](ota_error_t error) {
122 | if(error == OTA_AUTH_ERROR) events.send("Auth Failed", "ota");
123 | else if(error == OTA_BEGIN_ERROR) events.send("Begin Failed", "ota");
124 | else if(error == OTA_CONNECT_ERROR) events.send("Connect Failed", "ota");
125 | else if(error == OTA_RECEIVE_ERROR) events.send("Recieve Failed", "ota");
126 | else if(error == OTA_END_ERROR) events.send("End Failed", "ota");
127 | });
128 | ArduinoOTA.setHostname(hostName);
129 | ArduinoOTA.begin();
130 |
131 | MDNS.addService("http","tcp",80);
132 |
133 | SPIFFS.begin();
134 |
135 | ws.onEvent(onWsEvent);
136 | server.addHandler(&ws);
137 |
138 | events.onConnect([](AsyncEventSourceClient *client){
139 | client->send("hello!",NULL,millis(),1000);
140 | });
141 | server.addHandler(&events);
142 |
143 | #ifdef ESP32
144 | server.addHandler(new SPIFFSEditor(SPIFFS, http_username,http_password));
145 | #elif defined(ESP8266)
146 | server.addHandler(new SPIFFSEditor(http_username,http_password));
147 | #endif
148 |
149 | server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *request){
150 | request->send(200, "text/plain", String(ESP.getFreeHeap()));
151 | });
152 |
153 | server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.htm");
154 |
155 | server.onNotFound([](AsyncWebServerRequest *request){
156 | Serial.printf("NOT_FOUND: ");
157 | if(request->method() == HTTP_GET)
158 | Serial.printf("GET");
159 | else if(request->method() == HTTP_POST)
160 | Serial.printf("POST");
161 | else if(request->method() == HTTP_DELETE)
162 | Serial.printf("DELETE");
163 | else if(request->method() == HTTP_PUT)
164 | Serial.printf("PUT");
165 | else if(request->method() == HTTP_PATCH)
166 | Serial.printf("PATCH");
167 | else if(request->method() == HTTP_HEAD)
168 | Serial.printf("HEAD");
169 | else if(request->method() == HTTP_OPTIONS)
170 | Serial.printf("OPTIONS");
171 | else
172 | Serial.printf("UNKNOWN");
173 | Serial.printf(" http://%s%s\n", request->host().c_str(), request->url().c_str());
174 |
175 | if(request->contentLength()){
176 | Serial.printf("_CONTENT_TYPE: %s\n", request->contentType().c_str());
177 | Serial.printf("_CONTENT_LENGTH: %u\n", request->contentLength());
178 | }
179 |
180 | int headers = request->headers();
181 | int i;
182 | for(i=0;igetHeader(i);
184 | Serial.printf("_HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str());
185 | }
186 |
187 | int params = request->params();
188 | for(i=0;igetParam(i);
190 | if(p->isFile()){
191 | Serial.printf("_FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size());
192 | } else if(p->isPost()){
193 | Serial.printf("_POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
194 | } else {
195 | Serial.printf("_GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
196 | }
197 | }
198 |
199 | request->send(404);
200 | });
201 | server.onFileUpload([](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){
202 | if(!index)
203 | Serial.printf("UploadStart: %s\n", filename.c_str());
204 | Serial.printf("%s", (const char*)data);
205 | if(final)
206 | Serial.printf("UploadEnd: %s (%u)\n", filename.c_str(), index+len);
207 | });
208 | server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){
209 | if(!index)
210 | Serial.printf("BodyStart: %u\n", total);
211 | Serial.printf("%s", (const char*)data);
212 | if(index + len == total)
213 | Serial.printf("BodyEnd: %u\n", total);
214 | });
215 | server.begin();
216 | }
217 |
218 | void loop(){
219 | ArduinoOTA.handle();
220 | ws.cleanupClients();
221 | }
222 |
--------------------------------------------------------------------------------
/examples/ESP_AsyncFSBrowser/data/.exclude.files:
--------------------------------------------------------------------------------
1 | /*.js.gz
2 | /.exclude.files
3 |
--------------------------------------------------------------------------------
/examples/ESP_AsyncFSBrowser/data/ace.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dvarrel/ESPAsyncWebSrv/87424b3afe9a7df7828b5eb698efd68b6763705d/examples/ESP_AsyncFSBrowser/data/ace.js.gz
--------------------------------------------------------------------------------
/examples/ESP_AsyncFSBrowser/data/ext-searchbox.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dvarrel/ESPAsyncWebSrv/87424b3afe9a7df7828b5eb698efd68b6763705d/examples/ESP_AsyncFSBrowser/data/ext-searchbox.js.gz
--------------------------------------------------------------------------------
/examples/ESP_AsyncFSBrowser/data/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dvarrel/ESPAsyncWebSrv/87424b3afe9a7df7828b5eb698efd68b6763705d/examples/ESP_AsyncFSBrowser/data/favicon.ico
--------------------------------------------------------------------------------
/examples/ESP_AsyncFSBrowser/data/index.htm:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 |
22 | WebSocketTester
23 |
52 |
124 |
125 |
126 |
127 |
128 | $
129 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/examples/ESP_AsyncFSBrowser/data/mode-css.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dvarrel/ESPAsyncWebSrv/87424b3afe9a7df7828b5eb698efd68b6763705d/examples/ESP_AsyncFSBrowser/data/mode-css.js.gz
--------------------------------------------------------------------------------
/examples/ESP_AsyncFSBrowser/data/mode-html.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dvarrel/ESPAsyncWebSrv/87424b3afe9a7df7828b5eb698efd68b6763705d/examples/ESP_AsyncFSBrowser/data/mode-html.js.gz
--------------------------------------------------------------------------------
/examples/ESP_AsyncFSBrowser/data/mode-javascript.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dvarrel/ESPAsyncWebSrv/87424b3afe9a7df7828b5eb698efd68b6763705d/examples/ESP_AsyncFSBrowser/data/mode-javascript.js.gz
--------------------------------------------------------------------------------
/examples/ESP_AsyncFSBrowser/data/worker-html.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dvarrel/ESPAsyncWebSrv/87424b3afe9a7df7828b5eb698efd68b6763705d/examples/ESP_AsyncFSBrowser/data/worker-html.js.gz
--------------------------------------------------------------------------------
/examples/regex_patterns/.test.build_flags:
--------------------------------------------------------------------------------
1 | -DASYNCWEBSERVER_REGEX=1
2 |
--------------------------------------------------------------------------------
/examples/regex_patterns/regex_patterns.ino:
--------------------------------------------------------------------------------
1 | //
2 | // A simple server implementation with regex routes:
3 | // * serve static messages
4 | // * read GET and POST parameters
5 | // * handle missing pages / 404s
6 | //
7 |
8 | // Add buildflag ASYNCWEBSERVER_REGEX to enable the regex support
9 |
10 | // For platformio: platformio.ini:
11 | // build_flags =
12 | // -DASYNCWEBSERVER_REGEX
13 |
14 | // For arduino IDE: create/update platform.local.txt
15 | // Windows: C:\Users\(username)\AppData\Local\Arduino15\packages\espxxxx\hardware\espxxxx\{version}\platform.local.txt
16 | // Linux: ~/.arduino15/packages/espxxxx/hardware/espxxxx/{version}/platform.local.txt
17 | //
18 | // compiler.cpp.extra_flags=-DASYNCWEBSERVER_REGEX=1
19 |
20 | #include
21 | #ifdef ESP32
22 | #include
23 | #include
24 | #elif defined(ESP8266)
25 | #include
26 | #include
27 | #endif
28 | #include
29 |
30 | AsyncWebServer server(80);
31 |
32 | const char* ssid = "YOUR_SSID";
33 | const char* password = "YOUR_PASSWORD";
34 |
35 | const char* PARAM_MESSAGE = "message";
36 |
37 | void notFound(AsyncWebServerRequest *request) {
38 | request->send(404, "text/plain", "Not found");
39 | }
40 |
41 | void setup() {
42 |
43 | Serial.begin(115200);
44 | WiFi.mode(WIFI_STA);
45 | WiFi.begin(ssid, password);
46 | if (WiFi.waitForConnectResult() != WL_CONNECTED) {
47 | Serial.printf("WiFi Failed!\n");
48 | return;
49 | }
50 |
51 | Serial.print("IP Address: ");
52 | Serial.println(WiFi.localIP());
53 |
54 | server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
55 | request->send(200, "text/plain", "Hello, world");
56 | });
57 |
58 | // Send a GET request to /sensor/
59 | server.on("^\\/sensor\\/([0-9]+)$", HTTP_GET, [] (AsyncWebServerRequest *request) {
60 | String sensorNumber = request->pathArg(0);
61 | request->send(200, "text/plain", "Hello, sensor: " + sensorNumber);
62 | });
63 |
64 | // Send a GET request to /sensor//action/
65 | server.on("^\\/sensor\\/([0-9]+)\\/action\\/([a-zA-Z0-9]+)$", HTTP_GET, [] (AsyncWebServerRequest *request) {
66 | String sensorNumber = request->pathArg(0);
67 | String action = request->pathArg(1);
68 | request->send(200, "text/plain", "Hello, sensor: " + sensorNumber + ", with action: " + action);
69 | });
70 |
71 | server.onNotFound(notFound);
72 |
73 | server.begin();
74 | }
75 |
76 | void loop() {
77 | }
78 |
--------------------------------------------------------------------------------
/examples/simple_server/simple_server.ino:
--------------------------------------------------------------------------------
1 | //
2 | // A simple server implementation showing how to:
3 | // * serve static messages
4 | // * read GET and POST parameters
5 | // * handle missing pages / 404s
6 | //
7 |
8 | #include
9 | #ifdef ESP32
10 | #include
11 | #include
12 | #elif defined(ESP8266)
13 | #include
14 | #include
15 | #endif
16 | #include
17 |
18 | AsyncWebServer server(80);
19 |
20 | const char* ssid = "YOUR_SSID";
21 | const char* password = "YOUR_PASSWORD";
22 |
23 | const char* PARAM_MESSAGE = "message";
24 |
25 | void notFound(AsyncWebServerRequest *request) {
26 | request->send(404, "text/plain", "Not found");
27 | }
28 |
29 | void setup() {
30 |
31 | Serial.begin(115200);
32 | WiFi.mode(WIFI_STA);
33 | WiFi.begin(ssid, password);
34 | if (WiFi.waitForConnectResult() != WL_CONNECTED) {
35 | Serial.printf("WiFi Failed!\n");
36 | return;
37 | }
38 |
39 | Serial.print("IP Address: ");
40 | Serial.println(WiFi.localIP());
41 |
42 | server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
43 | request->send(200, "text/plain", "Hello, world");
44 | });
45 |
46 | // Send a GET request to /get?message=
47 | server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
48 | String message;
49 | if (request->hasParam(PARAM_MESSAGE)) {
50 | message = request->getParam(PARAM_MESSAGE)->value();
51 | } else {
52 | message = "No message sent";
53 | }
54 | request->send(200, "text/plain", "Hello, GET: " + message);
55 | });
56 |
57 | // Send a POST request to /post with a form field message set to
58 | server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request){
59 | String message;
60 | if (request->hasParam(PARAM_MESSAGE, true)) {
61 | message = request->getParam(PARAM_MESSAGE, true)->value();
62 | } else {
63 | message = "No message sent";
64 | }
65 | request->send(200, "text/plain", "Hello, POST: " + message);
66 | });
67 |
68 | server.onNotFound(notFound);
69 |
70 | server.begin();
71 | }
72 |
73 | void loop() {
74 | }
--------------------------------------------------------------------------------
/keywords.txt:
--------------------------------------------------------------------------------
1 | JsonArray KEYWORD1
2 | add KEYWORD2
3 | createArray KEYWORD3
4 |
--------------------------------------------------------------------------------
/library.properties:
--------------------------------------------------------------------------------
1 | name=ESPAsyncWebSrv
2 | version=1.2.9
3 | author=dvarrel
4 | maintainer=dvarrel
5 | sentence=Asynchronous HTTP and WebSocket Server Library for ESP8266 and ESP32 . Forked from https://github.com/me-no-dev/ESPAsyncWebServer
6 | paragraph=Build a WebServer, with files saved in flash
7 | category=Communication
8 | url=https://github.com/dvarrel/ESPAsyncWebSrv
9 | architectures=esp8266, esp32
10 | depends=AsyncTCP, ESPAsyncTCP
--------------------------------------------------------------------------------
/src/AsyncEventSource.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Asynchronous WebServer library for Espressif MCUs
3 |
4 | Copyright (c) 2016 Hristo Gochkov. All rights reserved.
5 |
6 | This library is free software; you can redistribute it and/or
7 | modify it under the terms of the GNU Lesser General Public
8 | License as published by the Free Software Foundation; either
9 | version 2.1 of the License, or (at your option) any later version.
10 |
11 | This library is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | Lesser General Public License for more details.
15 |
16 | You should have received a copy of the GNU Lesser General Public
17 | License along with this library; if not, write to the Free Software
18 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 | */
20 | #include "Arduino.h"
21 | #include "AsyncEventSource.h"
22 |
23 | static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect){
24 | String ev = "";
25 |
26 | if(reconnect){
27 | ev += "retry: ";
28 | ev += String(reconnect);
29 | ev += "\r\n";
30 | }
31 |
32 | if(id){
33 | ev += "id: ";
34 | ev += String(id);
35 | ev += "\r\n";
36 | }
37 |
38 | if(event != NULL){
39 | ev += "event: ";
40 | ev += String(event);
41 | ev += "\r\n";
42 | }
43 |
44 | if(message != NULL){
45 | size_t messageLen = strlen(message);
46 | char * lineStart = (char *)message;
47 | char * lineEnd;
48 | do {
49 | char * nextN = strchr(lineStart, '\n');
50 | char * nextR = strchr(lineStart, '\r');
51 | if(nextN == NULL && nextR == NULL){
52 | size_t llen = ((char *)message + messageLen) - lineStart;
53 | char * ldata = (char *)malloc(llen+1);
54 | if(ldata != NULL){
55 | memcpy(ldata, lineStart, llen);
56 | ldata[llen] = 0;
57 | ev += "data: ";
58 | ev += ldata;
59 | ev += "\r\n\r\n";
60 | free(ldata);
61 | }
62 | lineStart = (char *)message + messageLen;
63 | } else {
64 | char * nextLine = NULL;
65 | if(nextN != NULL && nextR != NULL){
66 | if(nextR < nextN){
67 | lineEnd = nextR;
68 | if(nextN == (nextR + 1))
69 | nextLine = nextN + 1;
70 | else
71 | nextLine = nextR + 1;
72 | } else {
73 | lineEnd = nextN;
74 | if(nextR == (nextN + 1))
75 | nextLine = nextR + 1;
76 | else
77 | nextLine = nextN + 1;
78 | }
79 | } else if(nextN != NULL){
80 | lineEnd = nextN;
81 | nextLine = nextN + 1;
82 | } else {
83 | lineEnd = nextR;
84 | nextLine = nextR + 1;
85 | }
86 |
87 | size_t llen = lineEnd - lineStart;
88 | char * ldata = (char *)malloc(llen+1);
89 | if(ldata != NULL){
90 | memcpy(ldata, lineStart, llen);
91 | ldata[llen] = 0;
92 | ev += "data: ";
93 | ev += ldata;
94 | ev += "\r\n";
95 | free(ldata);
96 | }
97 | lineStart = nextLine;
98 | if(lineStart == ((char *)message + messageLen))
99 | ev += "\r\n";
100 | }
101 | } while(lineStart < ((char *)message + messageLen));
102 | }
103 |
104 | return ev;
105 | }
106 |
107 | // Message
108 |
109 | AsyncEventSourceMessage::AsyncEventSourceMessage(const char * data, size_t len)
110 | : _data(nullptr), _len(len), _sent(0), _acked(0)
111 | {
112 | _data = (uint8_t*)malloc(_len+1);
113 | if(_data == nullptr){
114 | _len = 0;
115 | } else {
116 | memcpy(_data, data, len);
117 | _data[_len] = 0;
118 | }
119 | }
120 |
121 | AsyncEventSourceMessage::~AsyncEventSourceMessage() {
122 | if(_data != NULL)
123 | free(_data);
124 | }
125 |
126 | size_t AsyncEventSourceMessage::ack(size_t len, uint32_t time) {
127 | (void)time;
128 | // If the whole message is now acked...
129 | if(_acked + len > _len){
130 | // Return the number of extra bytes acked (they will be carried on to the next message)
131 | const size_t extra = _acked + len - _len;
132 | _acked = _len;
133 | return extra;
134 | }
135 | // Return that no extra bytes left.
136 | _acked += len;
137 | return 0;
138 | }
139 |
140 | size_t AsyncEventSourceMessage::send(AsyncClient *client) {
141 | const size_t len = _len - _sent;
142 | if(client->space() < len){
143 | return 0;
144 | }
145 | size_t sent = client->add((const char *)_data, len);
146 | if(client->canSend())
147 | client->send();
148 | _sent += sent;
149 | return sent;
150 | }
151 |
152 | // Client
153 |
154 | AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server)
155 | : _messageQueue(LinkedList([](AsyncEventSourceMessage *m){ delete m; }))
156 | {
157 | _client = request->client();
158 | _server = server;
159 | _lastId = 0;
160 | if(request->hasHeader("Last-Event-ID"))
161 | _lastId = atoi(request->getHeader("Last-Event-ID")->value().c_str());
162 |
163 | _client->setRxTimeout(0);
164 | _client->onError(NULL, NULL);
165 | _client->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; ((AsyncEventSourceClient*)(r))->_onAck(len, time); }, this);
166 | _client->onPoll([](void *r, AsyncClient* c){ (void)c; ((AsyncEventSourceClient*)(r))->_onPoll(); }, this);
167 | _client->onData(NULL, NULL);
168 | _client->onTimeout([this](void *r, AsyncClient* c __attribute__((unused)), uint32_t time){ ((AsyncEventSourceClient*)(r))->_onTimeout(time); }, this);
169 | _client->onDisconnect([this](void *r, AsyncClient* c){ ((AsyncEventSourceClient*)(r))->_onDisconnect(); delete c; }, this);
170 |
171 | _server->_addClient(this);
172 | delete request;
173 | }
174 |
175 | AsyncEventSourceClient::~AsyncEventSourceClient(){
176 | _messageQueue.free();
177 | close();
178 | }
179 |
180 | void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage){
181 | if(dataMessage == NULL)
182 | return;
183 | if(!connected()){
184 | delete dataMessage;
185 | return;
186 | }
187 | if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){
188 | //ets_printf("ERROR: Too many messages queued\n");
189 | delete dataMessage;
190 | } else {
191 | _messageQueue.add(dataMessage);
192 | }
193 | if(_client->canSend())
194 | _runQueue();
195 | }
196 |
197 | void AsyncEventSourceClient::_onAck(size_t len, uint32_t time){
198 | while(len && !_messageQueue.isEmpty()){
199 | len = _messageQueue.front()->ack(len, time);
200 | if(_messageQueue.front()->finished())
201 | _messageQueue.remove(_messageQueue.front());
202 | }
203 |
204 | _runQueue();
205 | }
206 |
207 | void AsyncEventSourceClient::_onPoll(){
208 | if(!_messageQueue.isEmpty()){
209 | _runQueue();
210 | }
211 | }
212 |
213 |
214 | void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))){
215 | _client->close(true);
216 | }
217 |
218 | void AsyncEventSourceClient::_onDisconnect(){
219 | _client = NULL;
220 | _server->_handleDisconnect(this);
221 | }
222 |
223 | void AsyncEventSourceClient::close(){
224 | if(_client != NULL)
225 | _client->close();
226 | }
227 |
228 | void AsyncEventSourceClient::write(const char * message, size_t len){
229 | _queueMessage(new AsyncEventSourceMessage(message, len));
230 | }
231 |
232 | void AsyncEventSourceClient::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){
233 | String ev = generateEventMessage(message, event, id, reconnect);
234 | _queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length()));
235 | }
236 |
237 | void AsyncEventSourceClient::_runQueue(){
238 | while(!_messageQueue.isEmpty() && _messageQueue.front()->finished()){
239 | _messageQueue.remove(_messageQueue.front());
240 | }
241 |
242 | for(auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i)
243 | {
244 | if(!(*i)->sent())
245 | (*i)->send(_client);
246 | }
247 | }
248 |
249 |
250 | // Handler
251 |
252 | AsyncEventSource::AsyncEventSource(const String& url)
253 | : _url(url)
254 | , _clients(LinkedList([](AsyncEventSourceClient *c){ delete c; }))
255 | , _connectcb(NULL)
256 | {}
257 |
258 | AsyncEventSource::~AsyncEventSource(){
259 | close();
260 | }
261 |
262 | void AsyncEventSource::onConnect(ArEventHandlerFunction cb){
263 | _connectcb = cb;
264 | }
265 |
266 | void AsyncEventSource::_addClient(AsyncEventSourceClient * client){
267 | /*char * temp = (char *)malloc(2054);
268 | if(temp != NULL){
269 | memset(temp+1,' ',2048);
270 | temp[0] = ':';
271 | temp[2049] = '\r';
272 | temp[2050] = '\n';
273 | temp[2051] = '\r';
274 | temp[2052] = '\n';
275 | temp[2053] = 0;
276 | client->write((const char *)temp, 2053);
277 | free(temp);
278 | }*/
279 |
280 | _clients.add(client);
281 | if(_connectcb)
282 | _connectcb(client);
283 | }
284 |
285 | void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client){
286 | _clients.remove(client);
287 | }
288 |
289 | void AsyncEventSource::close(){
290 | for(const auto &c: _clients){
291 | if(c->connected())
292 | c->close();
293 | }
294 | }
295 |
296 | // pmb fix
297 | size_t AsyncEventSource::avgPacketsWaiting() const {
298 | if(_clients.isEmpty())
299 | return 0;
300 |
301 | size_t aql=0;
302 | uint32_t nConnectedClients=0;
303 |
304 | for(const auto &c: _clients){
305 | if(c->connected()) {
306 | aql+=c->packetsWaiting();
307 | ++nConnectedClients;
308 | }
309 | }
310 | // return aql / nConnectedClients;
311 | return ((aql) + (nConnectedClients/2))/(nConnectedClients); // round up
312 | }
313 |
314 | void AsyncEventSource::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){
315 |
316 |
317 | String ev = generateEventMessage(message, event, id, reconnect);
318 | for(const auto &c: _clients){
319 | if(c->connected()) {
320 | c->write(ev.c_str(), ev.length());
321 | }
322 | }
323 | }
324 |
325 | size_t AsyncEventSource::count() const {
326 | return _clients.count_if([](AsyncEventSourceClient *c){
327 | return c->connected();
328 | });
329 | }
330 |
331 | bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){
332 | if(request->method() != HTTP_GET || !request->url().equals(_url)) {
333 | return false;
334 | }
335 | request->addInterestingHeader("Last-Event-ID");
336 | return true;
337 | }
338 |
339 | void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){
340 | if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
341 | return request->requestAuthentication();
342 | request->send(new AsyncEventSourceResponse(this));
343 | }
344 |
345 | // Response
346 |
347 | AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server){
348 | _server = server;
349 | _code = 200;
350 | _contentType = "text/event-stream";
351 | _sendContentLength = false;
352 | addHeader("Cache-Control", "no-cache");
353 | addHeader("Connection","keep-alive");
354 | }
355 |
356 | void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request){
357 | String out = _assembleHead(request->version());
358 | request->client()->write(out.c_str(), _headLength);
359 | _state = RESPONSE_WAIT_ACK;
360 | }
361 |
362 | size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time __attribute__((unused))){
363 | if(len){
364 | new AsyncEventSourceClient(request, _server);
365 | }
366 | return 0;
367 | }
368 |
369 |
--------------------------------------------------------------------------------
/src/AsyncEventSource.h:
--------------------------------------------------------------------------------
1 | /*
2 | Asynchronous WebServer library for Espressif MCUs
3 |
4 | Copyright (c) 2016 Hristo Gochkov. All rights reserved.
5 |
6 | This library is free software; you can redistribute it and/or
7 | modify it under the terms of the GNU Lesser General Public
8 | License as published by the Free Software Foundation; either
9 | version 2.1 of the License, or (at your option) any later version.
10 |
11 | This library is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | Lesser General Public License for more details.
15 |
16 | You should have received a copy of the GNU Lesser General Public
17 | License along with this library; if not, write to the Free Software
18 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 | */
20 | #ifndef ASYNCEVENTSOURCE_H_
21 | #define ASYNCEVENTSOURCE_H_
22 |
23 | #include
24 | #ifdef ESP32
25 | #include
26 | #define SSE_MAX_QUEUED_MESSAGES 32
27 | #else
28 | #include
29 | #define SSE_MAX_QUEUED_MESSAGES 8
30 | #endif
31 | #include
32 |
33 | #include "AsyncWebSynchronization.h"
34 |
35 | #ifdef ESP8266
36 | #include
37 | #ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
38 | #include <../src/Hash.h>
39 | #endif
40 | #endif
41 |
42 | #ifdef ESP32
43 | #define DEFAULT_MAX_SSE_CLIENTS 8
44 | #else
45 | #define DEFAULT_MAX_SSE_CLIENTS 4
46 | #endif
47 |
48 | class AsyncEventSource;
49 | class AsyncEventSourceResponse;
50 | class AsyncEventSourceClient;
51 | typedef std::function ArEventHandlerFunction;
52 |
53 | class AsyncEventSourceMessage {
54 | private:
55 | uint8_t * _data;
56 | size_t _len;
57 | size_t _sent;
58 | //size_t _ack;
59 | size_t _acked;
60 | public:
61 | AsyncEventSourceMessage(const char * data, size_t len);
62 | ~AsyncEventSourceMessage();
63 | size_t ack(size_t len, uint32_t time __attribute__((unused)));
64 | size_t send(AsyncClient *client);
65 | bool finished(){ return _acked == _len; }
66 | bool sent() { return _sent == _len; }
67 | };
68 |
69 | class AsyncEventSourceClient {
70 | private:
71 | AsyncClient *_client;
72 | AsyncEventSource *_server;
73 | uint32_t _lastId;
74 | LinkedList _messageQueue;
75 | void _queueMessage(AsyncEventSourceMessage *dataMessage);
76 | void _runQueue();
77 |
78 | public:
79 |
80 | AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server);
81 | ~AsyncEventSourceClient();
82 |
83 | AsyncClient* client(){ return _client; }
84 | void close();
85 | void write(const char * message, size_t len);
86 | void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
87 | bool connected() const { return (_client != NULL) && _client->connected(); }
88 | uint32_t lastId() const { return _lastId; }
89 | size_t packetsWaiting() const { return _messageQueue.length(); }
90 |
91 | //system callbacks (do not call)
92 | void _onAck(size_t len, uint32_t time);
93 | void _onPoll();
94 | void _onTimeout(uint32_t time);
95 | void _onDisconnect();
96 | };
97 |
98 | class AsyncEventSource: public AsyncWebHandler {
99 | private:
100 | String _url;
101 | LinkedList _clients;
102 | ArEventHandlerFunction _connectcb;
103 | public:
104 | AsyncEventSource(const String& url);
105 | ~AsyncEventSource();
106 |
107 | const char * url() const { return _url.c_str(); }
108 | void close();
109 | void onConnect(ArEventHandlerFunction cb);
110 | void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
111 | size_t count() const; //number clinets connected
112 | size_t avgPacketsWaiting() const;
113 |
114 | //system callbacks (do not call)
115 | void _addClient(AsyncEventSourceClient * client);
116 | void _handleDisconnect(AsyncEventSourceClient * client);
117 | virtual bool canHandle(AsyncWebServerRequest *request) override final;
118 | virtual void handleRequest(AsyncWebServerRequest *request) override final;
119 | };
120 |
121 | class AsyncEventSourceResponse: public AsyncWebServerResponse {
122 | private:
123 | String _content;
124 | AsyncEventSource *_server;
125 | public:
126 | AsyncEventSourceResponse(AsyncEventSource *server);
127 | void _respond(AsyncWebServerRequest *request);
128 | size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
129 | bool _sourceValid() const { return true; }
130 | };
131 |
132 |
133 | #endif /* ASYNCEVENTSOURCE_H_ */
134 |
--------------------------------------------------------------------------------
/src/AsyncJson.h:
--------------------------------------------------------------------------------
1 | // AsyncJson.h
2 | /*
3 | Async Response to use with ArduinoJson and AsyncWebServer
4 | Written by Andrew Melvin (SticilFace) with help from me-no-dev and BBlanchon.
5 |
6 | Example of callback in use
7 |
8 | server.on("/json", HTTP_ANY, [](AsyncWebServerRequest * request) {
9 |
10 | AsyncJsonResponse * response = new AsyncJsonResponse();
11 | JsonObject& root = response->getRoot();
12 | root["key1"] = "key number one";
13 | JsonObject& nested = root.createNestedObject("nested");
14 | nested["key1"] = "key number one";
15 |
16 | response->setLength();
17 | request->send(response);
18 | });
19 |
20 | --------------------
21 |
22 | Async Request to use with ArduinoJson and AsyncWebServer
23 | Written by Arsène von Wyss (avonwyss)
24 |
25 | Example
26 |
27 | AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint");
28 | handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) {
29 | JsonObject& jsonObj = json.as();
30 | // ...
31 | });
32 | server.addHandler(handler);
33 |
34 | */
35 | #ifndef ASYNC_JSON_H_
36 | #define ASYNC_JSON_H_
37 | #include
38 | #include
39 | #include
40 |
41 | #if ARDUINOJSON_VERSION_MAJOR == 5
42 | #define ARDUINOJSON_5_COMPATIBILITY
43 | #else
44 | #ifndef DYNAMIC_JSON_DOCUMENT_SIZE
45 | #define DYNAMIC_JSON_DOCUMENT_SIZE 1024
46 | #endif
47 | #endif
48 |
49 | constexpr const char* JSON_MIMETYPE = "application/json";
50 |
51 | /*
52 | * Json Response
53 | * */
54 |
55 | class ChunkPrint : public Print {
56 | private:
57 | uint8_t* _destination;
58 | size_t _to_skip;
59 | size_t _to_write;
60 | size_t _pos;
61 | public:
62 | ChunkPrint(uint8_t* destination, size_t from, size_t len)
63 | : _destination(destination), _to_skip(from), _to_write(len), _pos{0} {}
64 | virtual ~ChunkPrint(){}
65 | size_t write(uint8_t c){
66 | if (_to_skip > 0) {
67 | _to_skip--;
68 | return 1;
69 | } else if (_to_write > 0) {
70 | _to_write--;
71 | _destination[_pos++] = c;
72 | return 1;
73 | }
74 | return 0;
75 | }
76 | size_t write(const uint8_t *buffer, size_t size)
77 | {
78 | return this->Print::write(buffer, size);
79 | }
80 | };
81 |
82 | class AsyncJsonResponse: public AsyncAbstractResponse {
83 | protected:
84 |
85 | #ifdef ARDUINOJSON_5_COMPATIBILITY
86 | DynamicJsonBuffer _jsonBuffer;
87 | #else
88 | DynamicJsonDocument _jsonBuffer;
89 | #endif
90 |
91 | JsonVariant _root;
92 | bool _isValid;
93 |
94 | public:
95 |
96 | #ifdef ARDUINOJSON_5_COMPATIBILITY
97 | AsyncJsonResponse(bool isArray=false): _isValid{false} {
98 | _code = 200;
99 | _contentType = JSON_MIMETYPE;
100 | if(isArray)
101 | _root = _jsonBuffer.createArray();
102 | else
103 | _root = _jsonBuffer.createObject();
104 | }
105 | #else
106 | AsyncJsonResponse(bool isArray=false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : _jsonBuffer(maxJsonBufferSize), _isValid{false} {
107 | _code = 200;
108 | _contentType = JSON_MIMETYPE;
109 | if(isArray)
110 | _root = _jsonBuffer.createNestedArray();
111 | else
112 | _root = _jsonBuffer.createNestedObject();
113 | }
114 | #endif
115 |
116 | ~AsyncJsonResponse() {}
117 | JsonVariant & getRoot() { return _root; }
118 | bool _sourceValid() const { return _isValid; }
119 | size_t setLength() {
120 |
121 | #ifdef ARDUINOJSON_5_COMPATIBILITY
122 | _contentLength = _root.measureLength();
123 | #else
124 | _contentLength = measureJson(_root);
125 | #endif
126 |
127 | if (_contentLength) { _isValid = true; }
128 | return _contentLength;
129 | }
130 |
131 | size_t getSize() { return _jsonBuffer.size(); }
132 |
133 | size_t _fillBuffer(uint8_t *data, size_t len){
134 | ChunkPrint dest(data, _sentLength, len);
135 |
136 | #ifdef ARDUINOJSON_5_COMPATIBILITY
137 | _root.printTo( dest ) ;
138 | #else
139 | serializeJson(_root, dest);
140 | #endif
141 | return len;
142 | }
143 | };
144 |
145 | class PrettyAsyncJsonResponse: public AsyncJsonResponse {
146 | public:
147 | #ifdef ARDUINOJSON_5_COMPATIBILITY
148 | PrettyAsyncJsonResponse (bool isArray=false) : AsyncJsonResponse{isArray} {}
149 | #else
150 | PrettyAsyncJsonResponse (bool isArray=false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : AsyncJsonResponse{isArray, maxJsonBufferSize} {}
151 | #endif
152 | size_t setLength () {
153 | #ifdef ARDUINOJSON_5_COMPATIBILITY
154 | _contentLength = _root.measurePrettyLength ();
155 | #else
156 | _contentLength = measureJsonPretty(_root);
157 | #endif
158 | if (_contentLength) {_isValid = true;}
159 | return _contentLength;
160 | }
161 | size_t _fillBuffer (uint8_t *data, size_t len) {
162 | ChunkPrint dest (data, _sentLength, len);
163 | #ifdef ARDUINOJSON_5_COMPATIBILITY
164 | _root.prettyPrintTo (dest);
165 | #else
166 | serializeJsonPretty(_root, dest);
167 | #endif
168 | return len;
169 | }
170 | };
171 |
172 | typedef std::function ArJsonRequestHandlerFunction;
173 |
174 | class AsyncCallbackJsonWebHandler: public AsyncWebHandler {
175 | private:
176 | protected:
177 | const String _uri;
178 | WebRequestMethodComposite _method;
179 | ArJsonRequestHandlerFunction _onRequest;
180 | size_t _contentLength;
181 | #ifndef ARDUINOJSON_5_COMPATIBILITY
182 | const size_t maxJsonBufferSize;
183 | #endif
184 | size_t _maxContentLength;
185 | public:
186 | #ifdef ARDUINOJSON_5_COMPATIBILITY
187 | AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest)
188 | : _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {}
189 | #else
190 | AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize=DYNAMIC_JSON_DOCUMENT_SIZE)
191 | : _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {}
192 | #endif
193 |
194 | void setMethod(WebRequestMethodComposite method){ _method = method; }
195 | void setMaxContentLength(int maxContentLength){ _maxContentLength = maxContentLength; }
196 | void onRequest(ArJsonRequestHandlerFunction fn){ _onRequest = fn; }
197 |
198 | virtual bool canHandle(AsyncWebServerRequest *request) override final{
199 | if(!_onRequest)
200 | return false;
201 |
202 | if(!(_method & request->method()))
203 | return false;
204 |
205 | if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
206 | return false;
207 |
208 | if ( !request->contentType().equalsIgnoreCase(JSON_MIMETYPE) )
209 | return false;
210 |
211 | request->addInterestingHeader("ANY");
212 | return true;
213 | }
214 |
215 | virtual void handleRequest(AsyncWebServerRequest *request) override final {
216 | if(_onRequest) {
217 | if (request->_tempObject != NULL) {
218 |
219 | #ifdef ARDUINOJSON_5_COMPATIBILITY
220 | DynamicJsonBuffer jsonBuffer;
221 | JsonVariant json = jsonBuffer.parse((uint8_t*)(request->_tempObject));
222 | if (json.success()) {
223 | #else
224 | DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize);
225 | DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject));
226 | if(!error) {
227 | JsonVariant json = jsonBuffer.as();
228 | #endif
229 |
230 | _onRequest(request, json);
231 | return;
232 | }
233 | }
234 | request->send(_contentLength > _maxContentLength ? 413 : 400);
235 | } else {
236 | request->send(500);
237 | }
238 | }
239 | virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final {
240 | }
241 | virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
242 | if (_onRequest) {
243 | _contentLength = total;
244 | if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) {
245 | request->_tempObject = malloc(total);
246 | }
247 | if (request->_tempObject != NULL) {
248 | memcpy((uint8_t*)(request->_tempObject) + index, data, len);
249 | }
250 | }
251 | }
252 | virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
253 | };
254 | #endif
255 |
--------------------------------------------------------------------------------
/src/AsyncWebSocket.h:
--------------------------------------------------------------------------------
1 | /*
2 | Asynchronous WebServer library for Espressif MCUs
3 |
4 | Copyright (c) 2016 Hristo Gochkov. All rights reserved.
5 | This file is part of the esp8266 core for Arduino environment.
6 |
7 | This library is free software; you can redistribute it and/or
8 | modify it under the terms of the GNU Lesser General Public
9 | License as published by the Free Software Foundation; either
10 | version 2.1 of the License, or (at your option) any later version.
11 |
12 | This library is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | Lesser General Public License for more details.
16 |
17 | You should have received a copy of the GNU Lesser General Public
18 | License along with this library; if not, write to the Free Software
19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 | */
21 | #ifndef ASYNCWEBSOCKET_H_
22 | #define ASYNCWEBSOCKET_H_
23 |
24 | #include
25 | #ifdef ESP32
26 | #include
27 | #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
28 | // Code for version 3.x
29 | #include "mbedtls/compat-2.x.h"
30 | #endif
31 | #define WS_MAX_QUEUED_MESSAGES 32
32 | #else
33 | #include
34 | #define WS_MAX_QUEUED_MESSAGES 8
35 | #endif
36 | #include
37 |
38 | #include "AsyncWebSynchronization.h"
39 |
40 | #ifdef ESP8266
41 | #include
42 | #ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
43 | #include <../src/Hash.h>
44 | #endif
45 | #endif
46 |
47 | #ifdef ESP32
48 | #define DEFAULT_MAX_WS_CLIENTS 8
49 | #else
50 | #define DEFAULT_MAX_WS_CLIENTS 4
51 | #endif
52 |
53 | class AsyncWebSocket;
54 | class AsyncWebSocketResponse;
55 | class AsyncWebSocketClient;
56 | class AsyncWebSocketControl;
57 |
58 | typedef struct {
59 | /** Message type as defined by enum AwsFrameType.
60 | * Note: Applications will only see WS_TEXT and WS_BINARY.
61 | * All other types are handled by the library. */
62 | uint8_t message_opcode;
63 | /** Frame number of a fragmented message. */
64 | uint32_t num;
65 | /** Is this the last frame in a fragmented message ?*/
66 | uint8_t final;
67 | /** Is this frame masked? */
68 | uint8_t masked;
69 | /** Message type as defined by enum AwsFrameType.
70 | * This value is the same as message_opcode for non-fragmented
71 | * messages, but may also be WS_CONTINUATION in a fragmented message. */
72 | uint8_t opcode;
73 | /** Length of the current frame.
74 | * This equals the total length of the message if num == 0 && final == true */
75 | uint64_t len;
76 | /** Mask key */
77 | uint8_t mask[4];
78 | /** Offset of the data inside the current frame. */
79 | uint64_t index;
80 | } AwsFrameInfo;
81 |
82 | typedef enum { WS_DISCONNECTED, WS_CONNECTED, WS_DISCONNECTING } AwsClientStatus;
83 | typedef enum { WS_CONTINUATION, WS_TEXT, WS_BINARY, WS_DISCONNECT = 0x08, WS_PING, WS_PONG } AwsFrameType;
84 | typedef enum { WS_MSG_SENDING, WS_MSG_SENT, WS_MSG_ERROR } AwsMessageStatus;
85 | typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType;
86 |
87 | class AsyncWebSocketMessageBuffer {
88 | private:
89 | uint8_t * _data;
90 | size_t _len;
91 | bool _lock;
92 | uint32_t _count;
93 |
94 | public:
95 | AsyncWebSocketMessageBuffer();
96 | AsyncWebSocketMessageBuffer(size_t size);
97 | AsyncWebSocketMessageBuffer(uint8_t * data, size_t size);
98 | AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer &);
99 | AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBuffer &&);
100 | ~AsyncWebSocketMessageBuffer();
101 | void operator ++(int i) { (void)i; _count++; }
102 | void operator --(int i) { (void)i; if (_count > 0) { _count--; } ; }
103 | bool reserve(size_t size);
104 | void lock() { _lock = true; }
105 | void unlock() { _lock = false; }
106 | uint8_t * get() { return _data; }
107 | size_t length() { return _len; }
108 | uint32_t count() { return _count; }
109 | bool canDelete() { return (!_count && !_lock); }
110 |
111 | friend AsyncWebSocket;
112 |
113 | };
114 |
115 | class AsyncWebSocketMessage {
116 | protected:
117 | uint8_t _opcode;
118 | bool _mask;
119 | AwsMessageStatus _status;
120 | public:
121 | AsyncWebSocketMessage():_opcode(WS_TEXT),_mask(false),_status(WS_MSG_ERROR){}
122 | virtual ~AsyncWebSocketMessage(){}
123 | virtual void ack(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))){}
124 | virtual size_t send(AsyncClient *client __attribute__((unused))){ return 0; }
125 | virtual bool finished(){ return _status != WS_MSG_SENDING; }
126 | virtual bool betweenFrames() const { return false; }
127 | };
128 |
129 | class AsyncWebSocketBasicMessage: public AsyncWebSocketMessage {
130 | private:
131 | size_t _len;
132 | size_t _sent;
133 | size_t _ack;
134 | size_t _acked;
135 | uint8_t * _data;
136 | public:
137 | AsyncWebSocketBasicMessage(const char * data, size_t len, uint8_t opcode=WS_TEXT, bool mask=false);
138 | AsyncWebSocketBasicMessage(uint8_t opcode=WS_TEXT, bool mask=false);
139 | virtual ~AsyncWebSocketBasicMessage() override;
140 | virtual bool betweenFrames() const override { return _acked == _ack; }
141 | virtual void ack(size_t len, uint32_t time) override ;
142 | virtual size_t send(AsyncClient *client) override ;
143 | };
144 |
145 | class AsyncWebSocketMultiMessage: public AsyncWebSocketMessage {
146 | private:
147 | uint8_t * _data;
148 | size_t _len;
149 | size_t _sent;
150 | size_t _ack;
151 | size_t _acked;
152 | AsyncWebSocketMessageBuffer * _WSbuffer;
153 | public:
154 | AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuffer * buffer, uint8_t opcode=WS_TEXT, bool mask=false);
155 | virtual ~AsyncWebSocketMultiMessage() override;
156 | virtual bool betweenFrames() const override { return _acked == _ack; }
157 | virtual void ack(size_t len, uint32_t time) override ;
158 | virtual size_t send(AsyncClient *client) override ;
159 | };
160 |
161 | class AsyncWebSocketClient {
162 | private:
163 | AsyncClient *_client;
164 | AsyncWebSocket *_server;
165 | uint32_t _clientId;
166 | AwsClientStatus _status;
167 |
168 | LinkedList _controlQueue;
169 | LinkedList _messageQueue;
170 |
171 | uint8_t _pstate;
172 | AwsFrameInfo _pinfo;
173 |
174 | uint32_t _lastMessageTime;
175 | uint32_t _keepAlivePeriod;
176 |
177 | void _queueMessage(AsyncWebSocketMessage *dataMessage);
178 | void _queueControl(AsyncWebSocketControl *controlMessage);
179 | void _runQueue();
180 |
181 | public:
182 | void *_tempObject;
183 |
184 | AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server);
185 | ~AsyncWebSocketClient();
186 |
187 | //client id increments for the given server
188 | uint32_t id(){ return _clientId; }
189 | AwsClientStatus status(){ return _status; }
190 | AsyncClient* client(){ return _client; }
191 | AsyncWebSocket *server(){ return _server; }
192 | AwsFrameInfo const &pinfo() const { return _pinfo; }
193 |
194 | IPAddress remoteIP();
195 | uint16_t remotePort();
196 |
197 | //control frames
198 | void close(uint16_t code=0, const char * message=NULL);
199 | void ping(uint8_t *data=NULL, size_t len=0);
200 |
201 | //set auto-ping period in seconds. disabled if zero (default)
202 | void keepAlivePeriod(uint16_t seconds){
203 | _keepAlivePeriod = seconds * 1000;
204 | }
205 | uint16_t keepAlivePeriod(){
206 | return (uint16_t)(_keepAlivePeriod / 1000);
207 | }
208 |
209 | //data packets
210 | void message(AsyncWebSocketMessage *message){ _queueMessage(message); }
211 | bool queueIsFull();
212 |
213 | size_t printf(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
214 | #ifndef ESP32
215 | size_t printf_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3)));
216 | #endif
217 | void text(const char * message, size_t len);
218 | void text(const char * message);
219 | void text(uint8_t * message, size_t len);
220 | void text(char * message);
221 | void text(const String &message);
222 | void text(const __FlashStringHelper *data);
223 | void text(AsyncWebSocketMessageBuffer *buffer);
224 |
225 | void binary(const char * message, size_t len);
226 | void binary(const char * message);
227 | void binary(uint8_t * message, size_t len);
228 | void binary(char * message);
229 | void binary(const String &message);
230 | void binary(const __FlashStringHelper *data, size_t len);
231 | void binary(AsyncWebSocketMessageBuffer *buffer);
232 |
233 | bool canSend() { return _messageQueue.length() < WS_MAX_QUEUED_MESSAGES; }
234 |
235 | //system callbacks (do not call)
236 | void _onAck(size_t len, uint32_t time);
237 | void _onError(int8_t);
238 | void _onPoll();
239 | void _onTimeout(uint32_t time);
240 | void _onDisconnect();
241 | void _onData(void *pbuf, size_t plen);
242 | };
243 |
244 | typedef std::function AwsEventHandler;
245 |
246 | //WebServer Handler implementation that plays the role of a socket server
247 | class AsyncWebSocket: public AsyncWebHandler {
248 | public:
249 | typedef LinkedList AsyncWebSocketClientLinkedList;
250 | private:
251 | String _url;
252 | AsyncWebSocketClientLinkedList _clients;
253 | uint32_t _cNextId;
254 | AwsEventHandler _eventHandler;
255 | bool _enabled;
256 | AsyncWebLock _lock;
257 |
258 | public:
259 | AsyncWebSocket(const String& url);
260 | ~AsyncWebSocket();
261 | const char * url() const { return _url.c_str(); }
262 | void enable(bool e){ _enabled = e; }
263 | bool enabled() const { return _enabled; }
264 | bool availableForWriteAll();
265 | bool availableForWrite(uint32_t id);
266 |
267 | size_t count() const;
268 | AsyncWebSocketClient * client(uint32_t id);
269 | bool hasClient(uint32_t id){ return client(id) != NULL; }
270 |
271 | void close(uint32_t id, uint16_t code=0, const char * message=NULL);
272 | void closeAll(uint16_t code=0, const char * message=NULL);
273 | void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS);
274 |
275 | void ping(uint32_t id, uint8_t *data=NULL, size_t len=0);
276 | void pingAll(uint8_t *data=NULL, size_t len=0); // done
277 |
278 | void text(uint32_t id, const char * message, size_t len);
279 | void text(uint32_t id, const char * message);
280 | void text(uint32_t id, uint8_t * message, size_t len);
281 | void text(uint32_t id, char * message);
282 | void text(uint32_t id, const String &message);
283 | void text(uint32_t id, const __FlashStringHelper *message);
284 |
285 | void textAll(const char * message, size_t len);
286 | void textAll(const char * message);
287 | void textAll(uint8_t * message, size_t len);
288 | void textAll(char * message);
289 | void textAll(const String &message);
290 | void textAll(const __FlashStringHelper *message); // need to convert
291 | void textAll(AsyncWebSocketMessageBuffer * buffer);
292 |
293 | void binary(uint32_t id, const char * message, size_t len);
294 | void binary(uint32_t id, const char * message);
295 | void binary(uint32_t id, uint8_t * message, size_t len);
296 | void binary(uint32_t id, char * message);
297 | void binary(uint32_t id, const String &message);
298 | void binary(uint32_t id, const __FlashStringHelper *message, size_t len);
299 |
300 | void binaryAll(const char * message, size_t len);
301 | void binaryAll(const char * message);
302 | void binaryAll(uint8_t * message, size_t len);
303 | void binaryAll(char * message);
304 | void binaryAll(const String &message);
305 | void binaryAll(const __FlashStringHelper *message, size_t len);
306 | void binaryAll(AsyncWebSocketMessageBuffer * buffer);
307 |
308 | void message(uint32_t id, AsyncWebSocketMessage *message);
309 | void messageAll(AsyncWebSocketMultiMessage *message);
310 |
311 | size_t printf(uint32_t id, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
312 | size_t printfAll(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
313 | #ifndef ESP32
314 | size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__ ((format (printf, 3, 4)));
315 | #endif
316 | size_t printfAll_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3)));
317 |
318 | //event listener
319 | void onEvent(AwsEventHandler handler){
320 | _eventHandler = handler;
321 | }
322 |
323 | //system callbacks (do not call)
324 | uint32_t _getNextId(){ return _cNextId++; }
325 | void _addClient(AsyncWebSocketClient * client);
326 | void _handleDisconnect(AsyncWebSocketClient * client);
327 | void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len);
328 | virtual bool canHandle(AsyncWebServerRequest *request) override final;
329 | virtual void handleRequest(AsyncWebServerRequest *request) override final;
330 |
331 |
332 | // messagebuffer functions/objects.
333 | AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0);
334 | AsyncWebSocketMessageBuffer * makeBuffer(uint8_t * data, size_t size);
335 | LinkedList _buffers;
336 | void _cleanBuffers();
337 |
338 | AsyncWebSocketClientLinkedList getClients() const;
339 | };
340 |
341 | //WebServer response to authenticate the socket and detach the tcp client from the web server request
342 | class AsyncWebSocketResponse: public AsyncWebServerResponse {
343 | private:
344 | String _content;
345 | AsyncWebSocket *_server;
346 | public:
347 | AsyncWebSocketResponse(const String& key, AsyncWebSocket *server);
348 | void _respond(AsyncWebServerRequest *request);
349 | size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
350 | bool _sourceValid() const { return true; }
351 | };
352 |
353 |
354 | #endif /* ASYNCWEBSOCKET_H_ */
355 |
--------------------------------------------------------------------------------
/src/AsyncWebSynchronization.h:
--------------------------------------------------------------------------------
1 | #ifndef ASYNCWEBSYNCHRONIZATION_H_
2 | #define ASYNCWEBSYNCHRONIZATION_H_
3 |
4 | // Synchronisation is only available on ESP32, as the ESP8266 isn't using FreeRTOS by default
5 |
6 | #include
7 |
8 | #ifdef ESP32
9 |
10 | // This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore
11 | class AsyncWebLock
12 | {
13 | private:
14 | SemaphoreHandle_t _lock;
15 | mutable void *_lockedBy;
16 |
17 | public:
18 | AsyncWebLock() {
19 | _lock = xSemaphoreCreateBinary();
20 | _lockedBy = NULL;
21 | xSemaphoreGive(_lock);
22 | }
23 |
24 | ~AsyncWebLock() {
25 | vSemaphoreDelete(_lock);
26 | }
27 |
28 | bool lock() const {
29 | extern void *pxCurrentTCB;
30 | if (_lockedBy != pxCurrentTCB) {
31 | xSemaphoreTake(_lock, portMAX_DELAY);
32 | _lockedBy = pxCurrentTCB;
33 | return true;
34 | }
35 | return false;
36 | }
37 |
38 | void unlock() const {
39 | _lockedBy = NULL;
40 | xSemaphoreGive(_lock);
41 | }
42 | };
43 |
44 | #else
45 |
46 | // This is the 8266 version of the Sync Lock which is currently unimplemented
47 | class AsyncWebLock
48 | {
49 |
50 | public:
51 | AsyncWebLock() {
52 | }
53 |
54 | ~AsyncWebLock() {
55 | }
56 |
57 | bool lock() const {
58 | return false;
59 | }
60 |
61 | void unlock() const {
62 | }
63 | };
64 | #endif
65 |
66 | class AsyncWebLockGuard
67 | {
68 | private:
69 | const AsyncWebLock *_lock;
70 |
71 | public:
72 | AsyncWebLockGuard(const AsyncWebLock &l) {
73 | if (l.lock()) {
74 | _lock = &l;
75 | } else {
76 | _lock = NULL;
77 | }
78 | }
79 |
80 | ~AsyncWebLockGuard() {
81 | if (_lock) {
82 | _lock->unlock();
83 | }
84 | }
85 | };
86 |
87 | #endif // ASYNCWEBSYNCHRONIZATION_H_
--------------------------------------------------------------------------------
/src/ESPAsyncWebSrv.h:
--------------------------------------------------------------------------------
1 | /*
2 | Asynchronous WebServer library for Espressif MCUs
3 |
4 | Copyright (c) 2016 Hristo Gochkov. All rights reserved.
5 | This file is part of the esp8266 core for Arduino environment.
6 |
7 | This library is free software; you can redistribute it and/or
8 | modify it under the terms of the GNU Lesser General Public
9 | License as published by the Free Software Foundation; either
10 | version 2.1 of the License, or (at your option) any later version.
11 |
12 | This library is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | Lesser General Public License for more details.
16 |
17 | You should have received a copy of the GNU Lesser General Public
18 | License along with this library; if not, write to the Free Software
19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 | */
21 | #ifndef _ESPAsyncWebSrv_H_
22 | #define _ESPAsyncWebSrv_H_
23 |
24 | #include "Arduino.h"
25 |
26 | #include
27 | #include "FS.h"
28 |
29 | #include "StringArray.h"
30 |
31 | #ifdef ESP32
32 | #include
33 | #include
34 | #elif defined(ESP8266)
35 | #include
36 | #include
37 | #else
38 | #error Platform not supported
39 | #endif
40 |
41 | #ifdef ASYNCWEBSERVER_REGEX
42 | #define ASYNCWEBSERVER_REGEX_ATTRIBUTE
43 | #else
44 | #define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined")))
45 | #endif
46 |
47 | #define DEBUGF(...) //Serial.printf(__VA_ARGS__)
48 |
49 | class AsyncWebServer;
50 | class AsyncWebServerRequest;
51 | class AsyncWebServerResponse;
52 | class AsyncWebHeader;
53 | class AsyncWebParameter;
54 | class AsyncWebRewrite;
55 | class AsyncWebHandler;
56 | class AsyncStaticWebHandler;
57 | class AsyncCallbackWebHandler;
58 | class AsyncResponseStream;
59 |
60 | #ifndef WEBSERVER_H
61 | typedef enum {
62 | HTTP_GET = 0b00000001,
63 | HTTP_POST = 0b00000010,
64 | HTTP_DELETE = 0b00000100,
65 | HTTP_PUT = 0b00001000,
66 | HTTP_PATCH = 0b00010000,
67 | HTTP_HEAD = 0b00100000,
68 | HTTP_OPTIONS = 0b01000000,
69 | HTTP_ANY = 0b01111111,
70 | } WebRequestMethod;
71 | #endif
72 |
73 | //if this value is returned when asked for data, packet will not be sent and you will be asked for data again
74 | #define RESPONSE_TRY_AGAIN 0xFFFFFFFF
75 |
76 | typedef uint8_t WebRequestMethodComposite;
77 | typedef std::function ArDisconnectHandler;
78 |
79 | /*
80 | * PARAMETER :: Chainable object to hold GET/POST and FILE parameters
81 | * */
82 |
83 | class AsyncWebParameter {
84 | private:
85 | String _name;
86 | String _value;
87 | size_t _size;
88 | bool _isForm;
89 | bool _isFile;
90 |
91 | public:
92 |
93 | AsyncWebParameter(const String& name, const String& value, bool form=false, bool file=false, size_t size=0): _name(name), _value(value), _size(size), _isForm(form), _isFile(file){}
94 | const String& name() const { return _name; }
95 | const String& value() const { return _value; }
96 | size_t size() const { return _size; }
97 | bool isPost() const { return _isForm; }
98 | bool isFile() const { return _isFile; }
99 | };
100 |
101 | /*
102 | * HEADER :: Chainable object to hold the headers
103 | * */
104 |
105 | class AsyncWebHeader {
106 | private:
107 | String _name;
108 | String _value;
109 |
110 | public:
111 | AsyncWebHeader(const String& name, const String& value): _name(name), _value(value){}
112 | AsyncWebHeader(const String& data): _name(), _value(){
113 | if(!data) return;
114 | int index = data.indexOf(':');
115 | if (index < 0) return;
116 | _name = data.substring(0, index);
117 | _value = data.substring(index + 2);
118 | }
119 | ~AsyncWebHeader(){}
120 | const String& name() const { return _name; }
121 | const String& value() const { return _value; }
122 | String toString() const { return String(_name+": "+_value+"\r\n"); }
123 | };
124 |
125 | /*
126 | * REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect
127 | * */
128 |
129 | typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, RCT_MAX } RequestedConnectionType;
130 |
131 | typedef std::function AwsResponseFiller;
132 | typedef std::function AwsTemplateProcessor;
133 |
134 | class AsyncWebServerRequest {
135 | using File = fs::File;
136 | using FS = fs::FS;
137 | friend class AsyncWebServer;
138 | friend class AsyncCallbackWebHandler;
139 | private:
140 | AsyncClient* _client;
141 | AsyncWebServer* _server;
142 | AsyncWebHandler* _handler;
143 | AsyncWebServerResponse* _response;
144 | StringArray _interestingHeaders;
145 | ArDisconnectHandler _onDisconnectfn;
146 |
147 | String _temp;
148 | uint8_t _parseState;
149 |
150 | uint8_t _version;
151 | WebRequestMethodComposite _method;
152 | String _url;
153 | String _host;
154 | String _contentType;
155 | String _boundary;
156 | String _authorization;
157 | RequestedConnectionType _reqconntype;
158 | void _removeNotInterestingHeaders();
159 | bool _isDigest;
160 | bool _isMultipart;
161 | bool _isPlainPost;
162 | bool _expectingContinue;
163 | size_t _contentLength;
164 | size_t _parsedLength;
165 |
166 | LinkedList _headers;
167 | LinkedList _params;
168 | LinkedList _pathParams;
169 |
170 | uint8_t _multiParseState;
171 | uint8_t _boundaryPosition;
172 | size_t _itemStartIndex;
173 | size_t _itemSize;
174 | String _itemName;
175 | String _itemFilename;
176 | String _itemType;
177 | String _itemValue;
178 | uint8_t *_itemBuffer;
179 | size_t _itemBufferIndex;
180 | bool _itemIsFile;
181 |
182 | void _onPoll();
183 | void _onAck(size_t len, uint32_t time);
184 | void _onError(int8_t error);
185 | void _onTimeout(uint32_t time);
186 | void _onDisconnect();
187 | void _onData(void *buf, size_t len);
188 |
189 | void _addParam(AsyncWebParameter*);
190 | void _addPathParam(const char *param);
191 |
192 | bool _parseReqHead();
193 | bool _parseReqHeader();
194 | void _parseLine();
195 | void _parsePlainPostChar(uint8_t data);
196 | void _parseMultipartPostByte(uint8_t data, bool last);
197 | void _addGetParams(const String& params);
198 |
199 | void _handleUploadStart();
200 | void _handleUploadByte(uint8_t data, bool last);
201 | void _handleUploadEnd();
202 |
203 | public:
204 | File _tempFile;
205 | void *_tempObject;
206 |
207 | AsyncWebServerRequest(AsyncWebServer*, AsyncClient*);
208 | ~AsyncWebServerRequest();
209 |
210 | AsyncClient* client(){ return _client; }
211 | uint8_t version() const { return _version; }
212 | WebRequestMethodComposite method() const { return _method; }
213 | const String& url() const { return _url; }
214 | const String& host() const { return _host; }
215 | const String& contentType() const { return _contentType; }
216 | size_t contentLength() const { return _contentLength; }
217 | bool multipart() const { return _isMultipart; }
218 | const char * methodToString() const;
219 | const char * requestedConnTypeToString() const;
220 | RequestedConnectionType requestedConnType() const { return _reqconntype; }
221 | bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
222 | void onDisconnect (ArDisconnectHandler fn);
223 |
224 | //hash is the string representation of:
225 | // base64(user:pass) for basic or
226 | // user:realm:md5(user:realm:pass) for digest
227 | bool authenticate(const char * hash);
228 | bool authenticate(const char * username, const char * password, const char * realm = NULL, bool passwordIsHash = false);
229 | void requestAuthentication(const char * realm = NULL, bool isDigest = true);
230 |
231 | void setHandler(AsyncWebHandler *handler){ _handler = handler; }
232 | void addInterestingHeader(const String& name);
233 |
234 | void redirect(const String& url);
235 |
236 | void send(AsyncWebServerResponse *response);
237 | void send(int code, const String& contentType=String(), const String& content=String());
238 | void send(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
239 | void send(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
240 | void send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
241 | void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
242 | void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
243 | void send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
244 | void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr);
245 |
246 | AsyncWebServerResponse *beginResponse(int code, const String& contentType=String(), const String& content=String());
247 | AsyncWebServerResponse *beginResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
248 | AsyncWebServerResponse *beginResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
249 | AsyncWebServerResponse *beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
250 | AsyncWebServerResponse *beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
251 | AsyncWebServerResponse *beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
252 | AsyncResponseStream *beginResponseStream(const String& contentType, size_t bufferSize=1460);
253 | AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
254 | AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr);
255 |
256 | size_t headers() const; // get header count
257 | bool hasHeader(const String& name) const; // check if header exists
258 | bool hasHeader(const __FlashStringHelper * data) const; // check if header exists
259 |
260 | AsyncWebHeader* getHeader(const String& name) const;
261 | AsyncWebHeader* getHeader(const __FlashStringHelper * data) const;
262 | AsyncWebHeader* getHeader(size_t num) const;
263 |
264 | size_t params() const; // get arguments count
265 | bool hasParam(const String& name, bool post=false, bool file=false) const;
266 | bool hasParam(const __FlashStringHelper * data, bool post=false, bool file=false) const;
267 |
268 | AsyncWebParameter* getParam(const String& name, bool post=false, bool file=false) const;
269 | AsyncWebParameter* getParam(const __FlashStringHelper * data, bool post, bool file) const;
270 | AsyncWebParameter* getParam(size_t num) const;
271 |
272 | size_t args() const { return params(); } // get arguments count
273 | const String& arg(const String& name) const; // get request argument value by name
274 | const String& arg(const __FlashStringHelper * data) const; // get request argument value by F(name)
275 | const String& arg(size_t i) const; // get request argument value by number
276 | const String& argName(size_t i) const; // get request argument name by number
277 | bool hasArg(const char* name) const; // check if argument exists
278 | bool hasArg(const __FlashStringHelper * data) const; // check if F(argument) exists
279 |
280 | const String& ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const;
281 |
282 | const String& header(const char* name) const;// get request header value by name
283 | const String& header(const __FlashStringHelper * data) const;// get request header value by F(name)
284 | const String& header(size_t i) const; // get request header value by number
285 | const String& headerName(size_t i) const; // get request header name by number
286 | String urlDecode(const String& text) const;
287 | };
288 |
289 | /*
290 | * FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server)
291 | * */
292 |
293 | typedef std::function ArRequestFilterFunction;
294 |
295 | bool ON_STA_FILTER(AsyncWebServerRequest *request);
296 |
297 | bool ON_AP_FILTER(AsyncWebServerRequest *request);
298 |
299 | /*
300 | * REWRITE :: One instance can be handle any Request (done by the Server)
301 | * */
302 |
303 | class AsyncWebRewrite {
304 | protected:
305 | String _from;
306 | String _toUrl;
307 | String _params;
308 | ArRequestFilterFunction _filter;
309 | public:
310 | AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL){
311 | int index = _toUrl.indexOf('?');
312 | if (index > 0) {
313 | _params = _toUrl.substring(index +1);
314 | _toUrl = _toUrl.substring(0, index);
315 | }
316 | }
317 | virtual ~AsyncWebRewrite(){}
318 | AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; }
319 | bool filter(AsyncWebServerRequest *request) const { return _filter == NULL || _filter(request); }
320 | const String& from(void) const { return _from; }
321 | const String& toUrl(void) const { return _toUrl; }
322 | const String& params(void) const { return _params; }
323 | virtual bool match(AsyncWebServerRequest *request) { return from() == request->url() && filter(request); }
324 | };
325 |
326 | /*
327 | * HANDLER :: One instance can be attached to any Request (done by the Server)
328 | * */
329 |
330 | class AsyncWebHandler {
331 | protected:
332 | ArRequestFilterFunction _filter;
333 | String _username;
334 | String _password;
335 | public:
336 | AsyncWebHandler():_username(""), _password(""){}
337 | AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; }
338 | AsyncWebHandler& setAuthentication(const char *username, const char *password){ _username = String(username);_password = String(password); return *this; };
339 | bool filter(AsyncWebServerRequest *request){ return _filter == NULL || _filter(request); }
340 | virtual ~AsyncWebHandler(){}
341 | virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))){
342 | return false;
343 | }
344 | virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))){}
345 | virtual void handleUpload(AsyncWebServerRequest *request __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), bool final __attribute__((unused))){}
346 | virtual void handleBody(AsyncWebServerRequest *request __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))){}
347 | virtual bool isRequestHandlerTrivial(){return true;}
348 | };
349 |
350 | /*
351 | * RESPONSE :: One instance is created for each Request (attached by the Handler)
352 | * */
353 |
354 | typedef enum {
355 | RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED
356 | } WebResponseState;
357 |
358 | class AsyncWebServerResponse {
359 | protected:
360 | int _code;
361 | LinkedList _headers;
362 | String _contentType;
363 | size_t _contentLength;
364 | bool _sendContentLength;
365 | bool _chunked;
366 | size_t _headLength;
367 | size_t _sentLength;
368 | size_t _ackedLength;
369 | size_t _writtenLength;
370 | WebResponseState _state;
371 | const char* _responseCodeToString(int code);
372 |
373 | public:
374 | AsyncWebServerResponse();
375 | virtual ~AsyncWebServerResponse();
376 | virtual void setCode(int code);
377 | virtual void setContentLength(size_t len);
378 | virtual void setContentType(const String& type);
379 | virtual void addHeader(const String& name, const String& value);
380 | virtual String _assembleHead(uint8_t version);
381 | virtual bool _started() const;
382 | virtual bool _finished() const;
383 | virtual bool _failed() const;
384 | virtual bool _sourceValid() const;
385 | virtual void _respond(AsyncWebServerRequest *request);
386 | virtual size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
387 | };
388 |
389 | /*
390 | * SERVER :: One instance
391 | * */
392 |
393 | typedef std::function ArRequestHandlerFunction;
394 | typedef std::function ArUploadHandlerFunction;
395 | typedef std::function ArBodyHandlerFunction;
396 |
397 | class AsyncWebServer {
398 | protected:
399 | AsyncServer _server;
400 | LinkedList _rewrites;
401 | LinkedList _handlers;
402 | AsyncCallbackWebHandler* _catchAllHandler;
403 |
404 | public:
405 | AsyncWebServer(uint16_t port);
406 | ~AsyncWebServer();
407 |
408 | void begin();
409 | void end();
410 |
411 | #if ASYNC_TCP_SSL_ENABLED
412 | void onSslFileRequest(AcSSlFileHandler cb, void* arg);
413 | void beginSecure(const char *cert, const char *private_key_file, const char *password);
414 | #endif
415 |
416 | AsyncWebRewrite& addRewrite(AsyncWebRewrite* rewrite);
417 | bool removeRewrite(AsyncWebRewrite* rewrite);
418 | AsyncWebRewrite& rewrite(const char* from, const char* to);
419 |
420 | AsyncWebHandler& addHandler(AsyncWebHandler* handler);
421 | bool removeHandler(AsyncWebHandler* handler);
422 |
423 | AsyncCallbackWebHandler& on(const char* uri, ArRequestHandlerFunction onRequest);
424 | AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest);
425 | AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload);
426 | AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody);
427 |
428 | AsyncStaticWebHandler& serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control = NULL);
429 |
430 | void onNotFound(ArRequestHandlerFunction fn); //called when handler is not assigned
431 | void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads
432 | void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request)
433 |
434 | void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody
435 |
436 | void _handleDisconnect(AsyncWebServerRequest *request);
437 | void _attachHandler(AsyncWebServerRequest *request);
438 | void _rewriteRequest(AsyncWebServerRequest *request);
439 | };
440 |
441 | class DefaultHeaders {
442 | using headers_t = LinkedList;
443 | headers_t _headers;
444 |
445 | DefaultHeaders()
446 | :_headers(headers_t([](AsyncWebHeader *h){ delete h; }))
447 | {}
448 | public:
449 | using ConstIterator = headers_t::ConstIterator;
450 |
451 | void addHeader(const String& name, const String& value){
452 | _headers.add(new AsyncWebHeader(name, value));
453 | }
454 |
455 | ConstIterator begin() const { return _headers.begin(); }
456 | ConstIterator end() const { return _headers.end(); }
457 |
458 | DefaultHeaders(DefaultHeaders const &) = delete;
459 | DefaultHeaders &operator=(DefaultHeaders const &) = delete;
460 | static DefaultHeaders &Instance() {
461 | static DefaultHeaders instance;
462 | return instance;
463 | }
464 | };
465 |
466 | #include "WebResponseImpl.h"
467 | #include "WebHandlerImpl.h"
468 | #include "AsyncWebSocket.h"
469 | #include "AsyncEventSource.h"
470 |
471 | #endif /* _AsyncWebServer_H_ */
472 |
--------------------------------------------------------------------------------
/src/SPIFFSEditor.cpp:
--------------------------------------------------------------------------------
1 | #include "SPIFFSEditor.h"
2 | #include
3 |
4 | //File: edit.htm.gz, Size: 4151
5 | #define edit_htm_gz_len 4151
6 | const uint8_t edit_htm_gz[] PROGMEM = {
7 | 0x1F, 0x8B, 0x08, 0x08, 0xB8, 0x94, 0xB1, 0x59, 0x00, 0x03, 0x65, 0x64, 0x69, 0x74, 0x2E, 0x68,
8 | 0x74, 0x6D, 0x00, 0xB5, 0x3A, 0x0B, 0x7B, 0xDA, 0xB8, 0xB2, 0x7F, 0xC5, 0x71, 0xCF, 0x66, 0xED,
9 | 0x83, 0x31, 0x90, 0xA4, 0xD9, 0xD6, 0xC4, 0xC9, 0x42, 0x92, 0x36, 0x6D, 0xF3, 0x6A, 0x80, 0xB6,
10 | 0x69, 0x4F, 0xEE, 0x7E, 0xC2, 0x16, 0xA0, 0xC6, 0x96, 0x5D, 0x5B, 0x0E, 0x49, 0x59, 0xFE, 0xFB,
11 | 0x9D, 0x91, 0x6C, 0xB0, 0x09, 0x69, 0x77, 0xCF, 0xBD, 0xBB, 0xDD, 0x2D, 0x92, 0x46, 0x33, 0x9A,
12 | 0x19, 0xCD, 0x53, 0xDE, 0xBD, 0x8D, 0xA3, 0x8B, 0xC3, 0xFE, 0xF5, 0xE5, 0xB1, 0x36, 0x11, 0x61,
13 | 0xB0, 0xBF, 0x87, 0x7F, 0x6B, 0x01, 0xE1, 0x63, 0x97, 0xF2, 0xFD, 0x3D, 0xC1, 0x44, 0x40, 0xF7,
14 | 0x8F, 0x7B, 0x97, 0xDA, 0xB1, 0xCF, 0x44, 0x94, 0xEC, 0x35, 0xD4, 0xCA, 0x5E, 0x2A, 0x1E, 0x02,
15 | 0xAA, 0x85, 0xD4, 0x67, 0xC4, 0x4D, 0xBD, 0x84, 0xC2, 0x66, 0xDB, 0x0B, 0x67, 0xDF, 0xEB, 0x8C,
16 | 0xFB, 0xF4, 0xDE, 0xD9, 0x6E, 0x36, 0xDB, 0x71, 0x94, 0x32, 0xC1, 0x22, 0xEE, 0x90, 0x61, 0x1A,
17 | 0x05, 0x99, 0xA0, 0xED, 0x80, 0x8E, 0x84, 0xF3, 0x3C, 0xBE, 0x6F, 0x0F, 0xA3, 0xC4, 0xA7, 0x89,
18 | 0xD3, 0x8A, 0xEF, 0x35, 0x00, 0x31, 0x5F, 0x7B, 0xB6, 0xB3, 0xB3, 0xD3, 0x1E, 0x12, 0xEF, 0x76,
19 | 0x9C, 0x44, 0x19, 0xF7, 0xEB, 0x5E, 0x14, 0x44, 0x89, 0xF3, 0x6C, 0xF4, 0x1C, 0xFF, 0xB4, 0x7D,
20 | 0x96, 0xC6, 0x01, 0x79, 0x70, 0x78, 0xC4, 0x29, 0xE0, 0xDE, 0xD7, 0xD3, 0x09, 0xF1, 0xA3, 0xA9,
21 | 0xD3, 0xD4, 0x9A, 0x5A, 0xAB, 0x09, 0x44, 0x92, 0xF1, 0x90, 0x18, 0x4D, 0x0B, 0xFF, 0xD8, 0x3B,
22 | 0x66, 0x7B, 0x14, 0x71, 0x51, 0x4F, 0xD9, 0x77, 0xEA, 0xB4, 0xB6, 0xE0, 0x34, 0x39, 0x1D, 0x91,
23 | 0x90, 0x05, 0x0F, 0x4E, 0x4A, 0x78, 0x5A, 0x4F, 0x69, 0xC2, 0x46, 0x6A, 0x79, 0x4A, 0xD9, 0x78,
24 | 0x22, 0x9C, 0xDF, 0x9A, 0xCD, 0x39, 0xF0, 0xAF, 0x65, 0xC1, 0x2C, 0x60, 0x29, 0x20, 0xA3, 0x78,
25 | 0xEA, 0x3C, 0x11, 0xC5, 0x4E, 0x53, 0xB1, 0xDE, 0x6C, 0x87, 0x24, 0x19, 0x33, 0x0E, 0x83, 0x98,
26 | 0xF8, 0x3E, 0xE3, 0x63, 0x47, 0xA1, 0x05, 0x6C, 0xB6, 0x90, 0x36, 0xA1, 0x01, 0x11, 0xEC, 0x8E,
27 | 0xB6, 0x43, 0xC6, 0xEB, 0x53, 0xE6, 0x8B, 0x89, 0xB3, 0x0B, 0x3C, 0xB6, 0xBD, 0x2C, 0x49, 0x41,
28 | 0xA6, 0x38, 0x62, 0x5C, 0xD0, 0x44, 0xA2, 0xA5, 0x31, 0xE1, 0xB3, 0x5C, 0x54, 0x54, 0x40, 0x21,
29 | 0x27, 0xE3, 0x01, 0xE3, 0xB4, 0x3E, 0x0C, 0x22, 0xEF, 0x76, 0x71, 0xD2, 0x6E, 0x7C, 0x9F, 0x9F,
30 | 0xE5, 0x4C, 0xA2, 0x3B, 0x9A, 0xCC, 0x96, 0xEA, 0x92, 0xD8, 0x15, 0x60, 0x85, 0x34, 0xA5, 0x74,
31 | 0x6E, 0x8B, 0xBB, 0x0C, 0xA0, 0x96, 0xFC, 0x05, 0x29, 0x17, 0xFC, 0x2F, 0x45, 0x5A, 0x11, 0x5C,
32 | 0xA1, 0x30, 0x1E, 0x67, 0x62, 0xF6, 0xF8, 0x2A, 0xA3, 0x98, 0x78, 0x4C, 0x3C, 0xA0, 0xFC, 0xB0,
33 | 0x6D, 0x86, 0xBA, 0x04, 0xAC, 0x24, 0x24, 0x81, 0x86, 0x3A, 0xD7, 0x3E, 0xD0, 0xC4, 0x27, 0x9C,
34 | 0x58, 0x9D, 0x84, 0x91, 0xC0, 0xEA, 0x2D, 0xB5, 0x5E, 0x0F, 0xA3, 0xEF, 0xF5, 0x0C, 0xC6, 0x30,
35 | 0x0F, 0xA8, 0x27, 0x94, 0x92, 0xE1, 0x1E, 0x86, 0xB7, 0x4C, 0x3C, 0x06, 0x3C, 0x5A, 0x28, 0xA9,
36 | 0x4B, 0x2A, 0x69, 0xA2, 0x2E, 0xB0, 0x25, 0xD5, 0x83, 0x1C, 0x4B, 0xC9, 0x95, 0x50, 0xF5, 0x61,
37 | 0x24, 0x44, 0x14, 0x4A, 0x93, 0x5B, 0x08, 0xAC, 0x49, 0xAB, 0x79, 0xF1, 0xE8, 0x46, 0xD6, 0x6B,
38 | 0xBF, 0x44, 0xBE, 0x0D, 0x7A, 0x15, 0xCC, 0x23, 0x41, 0x9D, 0x04, 0x6C, 0xCC, 0x9D, 0x90, 0xF9,
39 | 0x7E, 0x40, 0x4B, 0x56, 0xEB, 0x64, 0x49, 0x60, 0xF8, 0x44, 0x10, 0x87, 0x85, 0x64, 0x4C, 0x1B,
40 | 0x31, 0x1F, 0x03, 0x34, 0xA5, 0xBB, 0x3B, 0x16, 0xFB, 0xD0, 0xBD, 0xB8, 0x9A, 0x36, 0xDF, 0xBD,
41 | 0x1E, 0x47, 0x1D, 0xF8, 0xE7, 0xBC, 0x37, 0x98, 0x1C, 0x0F, 0xC6, 0x30, 0xEA, 0xE2, 0xB4, 0xF3,
42 | 0xFE, 0xB0, 0xF3, 0x1E, 0x7E, 0x0E, 0x5B, 0xB5, 0xAF, 0xA3, 0x6F, 0xB8, 0xD0, 0x7D, 0xED, 0x77,
43 | 0xFB, 0x83, 0xE3, 0x4E, 0xE7, 0x5D, 0xE3, 0xCD, 0xF9, 0xF4, 0xE3, 0xBB, 0x5D, 0x04, 0x77, 0x83,
44 | 0xE6, 0xD5, 0x87, 0x49, 0x73, 0xB0, 0xF5, 0x32, 0xF4, 0x4F, 0xFC, 0x89, 0x17, 0x0E, 0x3A, 0xEF,
45 | 0x3F, 0x5E, 0xDD, 0x5D, 0x87, 0x83, 0x71, 0xEF, 0x63, 0x6B, 0xF2, 0x79, 0xEB, 0x43, 0xEF, 0xF3,
46 | 0xC7, 0x57, 0xB7, 0xF4, 0xD3, 0xC9, 0xDB, 0xCF, 0xFD, 0x29, 0x20, 0x1C, 0x45, 0xBD, 0xC1, 0x55,
47 | 0xF7, 0x43, 0x77, 0xFC, 0xB9, 0xEB, 0x1D, 0xDF, 0x0F, 0x83, 0xF3, 0xEE, 0xEB, 0xCE, 0xB0, 0xB3,
48 | 0xE5, 0x51, 0x3A, 0xEE, 0x5F, 0x75, 0xB3, 0x37, 0xEF, 0x2E, 0xC6, 0x8C, 0x4D, 0x7A, 0x9F, 0xCF,
49 | 0xFB, 0xDE, 0xE1, 0xF3, 0xD3, 0xC1, 0x49, 0x87, 0x4D, 0xCE, 0xDF, 0x5E, 0x35, 0x6F, 0x5F, 0xBF,
50 | 0x3B, 0x3C, 0xF2, 0xAE, 0xDF, 0x5E, 0xEF, 0x1E, 0x6D, 0x37, 0x7E, 0xFB, 0xED, 0xCC, 0xBF, 0x60,
51 | 0xBC, 0x7F, 0xF7, 0xBD, 0x33, 0x3E, 0x9C, 0xBE, 0x78, 0x48, 0xFB, 0x93, 0x37, 0x77, 0xBC, 0xF1,
52 | 0x21, 0xFA, 0xFA, 0xE6, 0xE1, 0x0C, 0xFE, 0xBB, 0xBC, 0xAC, 0x0D, 0x7B, 0xAD, 0x74, 0xF0, 0xFE,
53 | 0xCD, 0x87, 0xAD, 0xF4, 0xE5, 0xF3, 0xB8, 0x7B, 0x74, 0x74, 0x17, 0x0E, 0x2F, 0x1B, 0xA1, 0x7F,
54 | 0x3B, 0x12, 0x2F, 0xB6, 0x45, 0x7C, 0x3D, 0xCE, 0x3E, 0x7F, 0x7B, 0xFE, 0x76, 0xD2, 0xB8, 0xA0,
55 | 0xE4, 0x7A, 0x52, 0x7B, 0xF8, 0xFE, 0xF0, 0x62, 0xD2, 0x3F, 0xB9, 0x3B, 0x0F, 0xC8, 0xFD, 0xF9,
56 | 0xB9, 0xF7, 0x3D, 0xAC, 0x05, 0xE4, 0xE5, 0x45, 0x3F, 0x20, 0x49, 0x6B, 0xE0, 0x77, 0x1A, 0xB5,
57 | 0xC3, 0xAD, 0xCE, 0x8E, 0x48, 0xAE, 0x0E, 0xF9, 0xD1, 0xF6, 0xD7, 0xDE, 0x8B, 0x6E, 0xB7, 0x15,
58 | 0x0D, 0xBF, 0x6D, 0xBD, 0xBE, 0xDD, 0x7D, 0x3D, 0xD8, 0x7D, 0x3F, 0x7C, 0xDF, 0xE9, 0xED, 0x74,
59 | 0x07, 0xE4, 0xBA, 0xF7, 0xBE, 0x33, 0xDA, 0x19, 0x4E, 0x26, 0xEF, 0xDE, 0xF5, 0x5F, 0xF9, 0x9D,
60 | 0xEF, 0x49, 0xE7, 0x62, 0xDA, 0xB9, 0x3F, 0x1E, 0x74, 0x4E, 0x6A, 0xEF, 0x8E, 0xCF, 0x9A, 0xAD,
61 | 0xDE, 0xF5, 0xF6, 0xF8, 0x6C, 0x77, 0xDA, 0x4D, 0x8F, 0x3B, 0xEF, 0xBB, 0xCD, 0xF1, 0xDB, 0x5A,
62 | 0x48, 0x3E, 0x47, 0x87, 0xDB, 0xE3, 0x37, 0xBB, 0xEC, 0xF2, 0x9A, 0x74, 0xDE, 0x74, 0xDF, 0xA6,
63 | 0xEC, 0x2A, 0x3C, 0x19, 0x34, 0x3B, 0x9D, 0xD3, 0x0B, 0xFA, 0xEA, 0x70, 0x9B, 0xBC, 0xDB, 0xF2,
64 | 0x3E, 0x82, 0xFE, 0x07, 0x9F, 0xE8, 0x6F, 0xB5, 0xCE, 0xF4, 0xA2, 0x19, 0x78, 0x2F, 0x69, 0xFF,
65 | 0xE4, 0xBA, 0x2F, 0x6F, 0xE7, 0x38, 0x78, 0xD5, 0xBF, 0xED, 0x65, 0xEF, 0xC3, 0xC3, 0x43, 0x53,
66 | 0xE3, 0x51, 0x3D, 0xA1, 0x31, 0x25, 0xA2, 0x1C, 0xAE, 0x16, 0xFE, 0x01, 0xB6, 0xB5, 0xB4, 0xC2,
67 | 0xDC, 0x4F, 0x05, 0xBD, 0x17, 0x75, 0x9F, 0x7A, 0x51, 0x42, 0xE4, 0x1E, 0x40, 0xA0, 0x09, 0x9A,
68 | 0xD8, 0xFC, 0x77, 0x19, 0x3F, 0x35, 0x15, 0x3F, 0x35, 0xC2, 0x7D, 0xCD, 0x28, 0x1C, 0x01, 0x83,
69 | 0x87, 0x4F, 0xEF, 0x98, 0x47, 0xEB, 0x31, 0xBB, 0xA7, 0x41, 0x5D, 0x22, 0x3B, 0x4D, 0x73, 0x26,
70 | 0xFD, 0xAD, 0xD8, 0x46, 0x38, 0x98, 0x9A, 0xA4, 0x5A, 0x2C, 0xF8, 0x5F, 0x89, 0x47, 0x21, 0xB0,
71 | 0x81, 0xCB, 0x84, 0xF8, 0xAB, 0x7C, 0x27, 0x4A, 0xEA, 0xC3, 0x6C, 0x3C, 0x62, 0xF7, 0xE0, 0xD0,
72 | 0x23, 0xC6, 0x99, 0xA0, 0x5A, 0x2B, 0x9D, 0xFF, 0x5E, 0x90, 0xB9, 0xA5, 0x0F, 0xA3, 0x84, 0x84,
73 | 0x34, 0xD5, 0xFE, 0x22, 0x99, 0xD9, 0x28, 0x89, 0xC2, 0x65, 0x10, 0x99, 0x8B, 0xA8, 0x34, 0x99,
74 | 0xCF, 0x9F, 0x65, 0x71, 0x10, 0x11, 0x10, 0x73, 0x4D, 0xE4, 0x50, 0xF1, 0x34, 0x91, 0x6E, 0xB5,
75 | 0x88, 0xAB, 0xB9, 0x9B, 0x6D, 0xA1, 0x5B, 0x96, 0xDD, 0x7A, 0x6B, 0x67, 0xE9, 0xBA, 0x75, 0xB9,
76 | 0x17, 0xE3, 0xFD, 0x9A, 0x4C, 0x81, 0xF1, 0xA0, 0x14, 0xEE, 0x9E, 0x09, 0x50, 0xE9, 0x13, 0x87,
77 | 0xCB, 0x43, 0xF2, 0xC8, 0xB0, 0x60, 0x40, 0x05, 0xEA, 0x96, 0x8C, 0xD4, 0x85, 0x24, 0xB0, 0x6F,
78 | 0xFE, 0x8C, 0xCA, 0xBC, 0x67, 0x3D, 0x8B, 0x13, 0xB8, 0x0D, 0x3A, 0xFD, 0x11, 0xCD, 0x42, 0xA6,
79 | 0x2A, 0x6D, 0x45, 0x53, 0x65, 0xBC, 0x5C, 0x84, 0x65, 0xDA, 0x93, 0xBC, 0x16, 0xA4, 0x1F, 0x4B,
80 | 0x05, 0xE0, 0x05, 0x37, 0xCF, 0x91, 0x9B, 0x1F, 0x6A, 0x75, 0x7B, 0xF7, 0x97, 0x9C, 0x87, 0x9D,
81 | 0xE6, 0x2F, 0x73, 0x3B, 0xDF, 0x5B, 0xA4, 0xE4, 0x56, 0x13, 0xFE, 0x29, 0x32, 0xEF, 0x8B, 0x25,
82 | 0x0B, 0xC3, 0xE7, 0xF8, 0xA7, 0x60, 0x10, 0xE9, 0x94, 0x80, 0xDB, 0x3B, 0x2F, 0x5F, 0xF8, 0xC3,
83 | 0x02, 0x98, 0x0B, 0xF6, 0x24, 0x3C, 0x21, 0x3E, 0xCB, 0x52, 0xE7, 0x79, 0xF3, 0x97, 0x5C, 0x9F,
84 | 0x5B, 0x3B, 0x28, 0xFB, 0xE2, 0x2E, 0x71, 0xB2, 0xB4, 0xD8, 0x34, 0x66, 0x5C, 0xDB, 0x4A, 0x35,
85 | 0xBC, 0x6F, 0x92, 0x2C, 0x0C, 0xB3, 0x92, 0xED, 0xE7, 0xBF, 0x2F, 0x4D, 0x13, 0xF7, 0xCF, 0x9A,
86 | 0xBF, 0xCC, 0x44, 0x02, 0xD9, 0x64, 0x04, 0xB9, 0xC6, 0x49, 0x22, 0x41, 0x04, 0x35, 0x9A, 0xE6,
87 | 0x1C, 0x84, 0x5B, 0x03, 0xD8, 0xDE, 0x6D, 0xFA, 0x74, 0x6C, 0xCE, 0xE7, 0x7B, 0x0D, 0x99, 0xD7,
88 | 0xA0, 0x6C, 0xF1, 0x12, 0x16, 0x8B, 0xFD, 0x51, 0xC6, 0x3D, 0xE4, 0x41, 0x1B, 0x53, 0x83, 0x9A,
89 | 0xB3, 0x84, 0x8A, 0x2C, 0xE1, 0x9A, 0x1F, 0x79, 0x19, 0x1A, 0xBB, 0x3D, 0xA6, 0xE2, 0x58, 0xD9,
90 | 0x7D, 0xF7, 0xE1, 0x8D, 0x0F, 0x3B, 0xE6, 0x0B, 0x04, 0x6F, 0x2D, 0x02, 0x38, 0x30, 0x9C, 0x97,
91 | 0xE3, 0x54, 0xF6, 0x43, 0x82, 0x01, 0x22, 0xEF, 0xE8, 0x83, 0x41, 0x2D, 0xB1, 0x40, 0xA4, 0x36,
92 | 0xAE, 0x1B, 0xC5, 0x2E, 0x80, 0x71, 0x73, 0x76, 0x07, 0x4A, 0x20, 0x2E, 0xFD, 0x22, 0x6E, 0x2C,
93 | 0xE6, 0x72, 0xF8, 0x69, 0xE7, 0xBB, 0xC9, 0x1E, 0x3B, 0xA8, 0xB7, 0x1C, 0xB2, 0xCF, 0x0E, 0x5A,
94 | 0xE0, 0x5E, 0x65, 0x6E, 0xE4, 0xB9, 0xAF, 0x58, 0x40, 0x07, 0xB9, 0xC3, 0xE1, 0x31, 0x48, 0x6C,
95 | 0xB1, 0x85, 0x28, 0xE2, 0x5B, 0xCD, 0xE6, 0x86, 0x4B, 0x0F, 0x48, 0x00, 0x39, 0xCC, 0xD0, 0x8F,
96 | 0xAF, 0xAE, 0x2E, 0xAE, 0xBE, 0xE8, 0x35, 0x5A, 0xD3, 0x6F, 0x1C, 0x4D, 0xAF, 0x71, 0xD3, 0x11,
97 | 0x76, 0x42, 0x47, 0x09, 0x4D, 0x27, 0x97, 0x44, 0x4C, 0x8C, 0xD4, 0xBE, 0x23, 0x41, 0x56, 0x16,
98 | 0x84, 0xA1, 0xDC, 0xC8, 0xA2, 0x70, 0x39, 0x9D, 0x6A, 0xAF, 0x40, 0xCD, 0x47, 0x90, 0xEA, 0xDA,
99 | 0xC2, 0x26, 0x71, 0x4C, 0xB9, 0x6F, 0xE8, 0x31, 0x20, 0xEA, 0x16, 0x35, 0xAD, 0x84, 0x7E, 0xCB,
100 | 0x68, 0x2A, 0x52, 0x1B, 0x2C, 0xD7, 0xD0, 0x2F, 0x07, 0x7D, 0xDD, 0xD2, 0x1B, 0xE8, 0x47, 0x3A,
101 | 0xF0, 0x46, 0xCC, 0x39, 0x52, 0x89, 0x5C, 0xD0, 0xA4, 0x3E, 0xCC, 0xC0, 0xA0, 0xB8, 0x6E, 0xB6,
102 | 0x23, 0x9B, 0x71, 0x4E, 0x93, 0x93, 0xFE, 0xD9, 0xA9, 0xAB, 0x5F, 0x29, 0x46, 0xB4, 0x53, 0x28,
103 | 0x48, 0x74, 0x4B, 0x5E, 0x51, 0x7E, 0xC8, 0xE1, 0x84, 0x05, 0xBE, 0x11, 0x99, 0x6D, 0x24, 0xE1,
104 | 0x49, 0x12, 0xB2, 0x40, 0x01, 0x0A, 0x9E, 0x2D, 0x1E, 0x62, 0xEA, 0xEA, 0x23, 0x50, 0x86, 0x6E,
105 | 0x79, 0x76, 0x98, 0x05, 0x82, 0xC5, 0x01, 0x75, 0x37, 0x5A, 0x30, 0xE3, 0x60, 0x41, 0xAE, 0x8E,
106 | 0xB9, 0x19, 0x61, 0xCC, 0x77, 0x75, 0x15, 0xA1, 0xF2, 0xB8, 0xB6, 0xEE, 0x14, 0x4F, 0x9D, 0x92,
107 | 0x56, 0x4E, 0x49, 0xCB, 0xB8, 0x4A, 0xE0, 0x34, 0x3F, 0x18, 0xC3, 0x3C, 0xCE, 0xD4, 0x51, 0x05,
108 | 0xCC, 0xA7, 0x23, 0x02, 0x9C, 0x7C, 0x40, 0x6D, 0xBA, 0x7A, 0x63, 0xDD, 0x41, 0xA9, 0x3A, 0xC8,
109 | 0xAF, 0x6A, 0xC4, 0x2F, 0x6B, 0x44, 0xDD, 0xEE, 0x3A, 0x64, 0x5F, 0x21, 0x07, 0x55, 0xE4, 0xA0,
110 | 0x8C, 0x7C, 0x28, 0x8D, 0x64, 0x1D, 0x72, 0xA0, 0x90, 0x93, 0x8A, 0x88, 0x89, 0x14, 0x51, 0x85,
111 | 0xBD, 0x3A, 0x6A, 0x13, 0x05, 0xD2, 0xAD, 0xA4, 0x22, 0x66, 0x62, 0x83, 0x97, 0x92, 0x61, 0x40,
112 | 0x7D, 0x77, 0xA3, 0x09, 0x33, 0x2C, 0xB6, 0xDD, 0xAD, 0xE6, 0x9A, 0x33, 0x12, 0x75, 0x46, 0x56,
113 | 0x65, 0x30, 0x2B, 0x33, 0xA8, 0xF5, 0xC8, 0x1D, 0xD5, 0xD6, 0x31, 0x98, 0x99, 0x56, 0x60, 0x47,
114 | 0xDC, 0x0B, 0x98, 0x77, 0xEB, 0x2E, 0xBD, 0xC5, 0x9C, 0xB1, 0x85, 0x85, 0x5A, 0x5C, 0x06, 0xBA,
115 | 0x01, 0x94, 0x5E, 0x8B, 0xA5, 0x7C, 0x80, 0xFA, 0x9E, 0x5B, 0xD9, 0x5A, 0x02, 0xDC, 0xA6, 0xF7,
116 | 0xD4, 0x3B, 0x8C, 0xC2, 0x90, 0xA0, 0xED, 0xA6, 0xC0, 0x41, 0x3E, 0xD1, 0xCD, 0xB9, 0x15, 0xAD,
117 | 0xC5, 0x79, 0xC2, 0x45, 0x2C, 0x7F, 0x3D, 0x8B, 0x23, 0x03, 0x5C, 0xCE, 0xF5, 0x6C, 0xD4, 0x61,
118 | 0x6A, 0x83, 0x1E, 0xC7, 0x62, 0xF2, 0x13, 0x17, 0x2A, 0x0C, 0x54, 0xA2, 0x7C, 0x69, 0xDE, 0x58,
119 | 0x0B, 0x91, 0x56, 0x7C, 0xEA, 0xA2, 0xB7, 0xE2, 0x54, 0xA8, 0xBC, 0x8A, 0x5D, 0x9A, 0x4B, 0x1D,
120 | 0x94, 0x61, 0xB9, 0xBD, 0x2F, 0xA0, 0xFA, 0x7C, 0x0E, 0xE7, 0x01, 0xFF, 0x13, 0x68, 0xF9, 0xE8,
121 | 0x5F, 0x17, 0x60, 0xC9, 0xA3, 0x34, 0x78, 0x8B, 0xBB, 0x0D, 0xE3, 0xC0, 0xF9, 0x8F, 0x6D, 0x7C,
122 | 0xF9, 0x1F, 0xFB, 0xA6, 0x66, 0x9A, 0x07, 0xFF, 0x6A, 0x48, 0x0D, 0x1B, 0xC2, 0xFC, 0xD2, 0xBA,
123 | 0xB1, 0x08, 0x80, 0xED, 0x7F, 0x9B, 0xFF, 0xB1, 0x25, 0xB8, 0x02, 0x6B, 0xDF, 0x45, 0x90, 0x49,
124 | 0xF0, 0x24, 0x34, 0xB0, 0x68, 0xA4, 0x91, 0xCD, 0x4D, 0x43, 0xB8, 0xA4, 0x72, 0x8D, 0x35, 0x51,
125 | 0xD3, 0x6D, 0x88, 0x53, 0x50, 0x5B, 0xAC, 0x04, 0xBF, 0x3E, 0x24, 0x7A, 0x15, 0x5B, 0x17, 0x00,
126 | 0xC9, 0x3D, 0xCA, 0x0C, 0x3D, 0x22, 0x97, 0x52, 0xCB, 0x0C, 0x02, 0x42, 0xA7, 0x89, 0xE7, 0x2A,
127 | 0xAD, 0x1D, 0x14, 0x30, 0x17, 0xA2, 0xE0, 0xBC, 0x1C, 0x2D, 0x15, 0xEA, 0xAA, 0xFD, 0x17, 0x0A,
128 | 0xA3, 0xD6, 0x12, 0x8A, 0x04, 0x31, 0xAD, 0xD8, 0x79, 0xC6, 0x72, 0x75, 0x4C, 0x59, 0xBA, 0x35,
129 | 0x59, 0x5D, 0x96, 0xAD, 0x04, 0xAE, 0x2F, 0x8D, 0xFE, 0xD7, 0x3D, 0x16, 0x8E, 0xB5, 0x12, 0x3F,
130 | 0xF8, 0x97, 0xFB, 0x2B, 0x46, 0xE4, 0xCD, 0x3F, 0xBC, 0x21, 0x70, 0x05, 0xA6, 0x41, 0x6D, 0x1E,
131 | 0x4D, 0x0D, 0xB3, 0xF6, 0xAB, 0xAE, 0x49, 0x8A, 0xAE, 0x1E, 0x92, 0xFB, 0xBC, 0xA7, 0xC4, 0x8C,
132 | 0xD7, 0xD6, 0x70, 0x5E, 0xB4, 0x28, 0xF9, 0x82, 0xEC, 0xE6, 0x48, 0x26, 0xA2, 0xB6, 0x56, 0x64,
133 | 0x52, 0xD5, 0xCA, 0xE8, 0x5A, 0x63, 0xFF, 0xD7, 0x4A, 0x40, 0xB7, 0x98, 0xBA, 0x4E, 0x15, 0x8C,
134 | 0xB3, 0x00, 0x1C, 0x93, 0x3E, 0x1D, 0x69, 0x03, 0x26, 0x03, 0x75, 0x35, 0x46, 0x5A, 0x81, 0xC1,
135 | 0xCC, 0x03, 0xC3, 0x2B, 0xFB, 0xF3, 0x1E, 0x16, 0xBF, 0xFB, 0x97, 0xAA, 0xAA, 0x81, 0xD4, 0x8B,
136 | 0x33, 0x5D, 0x59, 0x59, 0xD5, 0x4B, 0xE0, 0xD2, 0x08, 0xA0, 0x5B, 0x8B, 0x3C, 0x3A, 0x8C, 0xFC,
137 | 0x87, 0x52, 0xF6, 0x4D, 0xBB, 0x0F, 0x87, 0x01, 0x49, 0xD3, 0x73, 0xB8, 0x01, 0x43, 0xF7, 0x42,
138 | 0x50, 0xB8, 0xB2, 0xC2, 0xFD, 0xE6, 0xE6, 0x66, 0x15, 0x29, 0xA1, 0x21, 0x14, 0xDB, 0x8A, 0x2B,
139 | 0xF0, 0x49, 0xD3, 0xF1, 0x81, 0x30, 0x18, 0xD2, 0x1A, 0xC6, 0xF0, 0x25, 0xE3, 0x47, 0x5C, 0x71,
140 | 0xF4, 0xF4, 0x22, 0xA6, 0xFC, 0x33, 0xDC, 0x95, 0x32, 0xCB, 0x1A, 0xAD, 0xA6, 0x68, 0xFA, 0x8F,
141 | 0xD8, 0x3E, 0xCA, 0x0D, 0x76, 0xC1, 0x7A, 0xBA, 0x56, 0xA1, 0xFC, 0x9F, 0x61, 0xB9, 0x94, 0x28,
142 | 0xD6, 0x70, 0x9C, 0x40, 0x80, 0x5A, 0xC3, 0x31, 0xC4, 0x1A, 0x41, 0x17, 0xFC, 0x26, 0x6B, 0xF9,
143 | 0xCD, 0xFE, 0x19, 0x7E, 0x97, 0x76, 0x1E, 0x15, 0x25, 0x91, 0xAA, 0xAF, 0x50, 0x02, 0x9F, 0xDD,
144 | 0xE9, 0xA6, 0x15, 0xB9, 0x55, 0x0A, 0x50, 0x1B, 0x46, 0x41, 0xD0, 0x8F, 0xE2, 0x83, 0x27, 0xD6,
145 | 0x9D, 0xC5, 0x7A, 0x31, 0xC8, 0xD9, 0x5C, 0x6E, 0xB1, 0xBC, 0xB5, 0x44, 0x4F, 0xA1, 0xEC, 0x5F,
146 | 0x4B, 0x15, 0x01, 0x3F, 0x23, 0x8B, 0x7B, 0xAC, 0xD4, 0xA5, 0x36, 0x28, 0x0F, 0x56, 0x3F, 0xD5,
147 | 0x3C, 0xCB, 0x5F, 0xCC, 0xAE, 0x6B, 0x51, 0x9B, 0xC0, 0x38, 0x57, 0x92, 0x8B, 0x4A, 0xB2, 0xC8,
148 | 0x13, 0x01, 0xA8, 0x58, 0xC7, 0x2E, 0xC4, 0x4D, 0x6B, 0x7A, 0x7C, 0xBF, 0x5C, 0x83, 0xC2, 0xDF,
149 | 0xF5, 0xD5, 0x12, 0x33, 0x08, 0xC4, 0xD3, 0x95, 0x4B, 0x29, 0x5F, 0x37, 0x29, 0x8A, 0x0E, 0x62,
150 | 0x47, 0xA3, 0x51, 0x4A, 0xC5, 0x47, 0x0C, 0x49, 0x56, 0xB2, 0x98, 0x9F, 0xC8, 0x90, 0x04, 0x8C,
151 | 0x45, 0x3C, 0x8C, 0xB2, 0x94, 0x46, 0x99, 0xA8, 0xA4, 0x16, 0x63, 0x21, 0xCC, 0x5E, 0xFA, 0xE7,
152 | 0x9F, 0x8B, 0xC9, 0x7E, 0x5A, 0x0B, 0x96, 0xD3, 0xEB, 0x3D, 0xBF, 0x34, 0xD9, 0xF7, 0x6B, 0x89,
153 | 0xB9, 0x7A, 0xE9, 0xFF, 0x67, 0x4B, 0x21, 0x65, 0x4B, 0xF1, 0xB0, 0x54, 0x2E, 0x62, 0x62, 0x29,
154 | 0xE6, 0xC9, 0x82, 0x91, 0x97, 0x7C, 0x16, 0x0D, 0x1A, 0x2B, 0x25, 0x55, 0x9E, 0x97, 0x7D, 0x95,
155 | 0x43, 0x40, 0x59, 0x71, 0xE5, 0x35, 0x11, 0x06, 0x34, 0xE0, 0x63, 0x64, 0xF2, 0x41, 0xEB, 0xA7,
156 | 0xD1, 0x94, 0x26, 0x87, 0x24, 0xA5, 0x06, 0x24, 0xCD, 0x65, 0xDC, 0x41, 0xA8, 0xE9, 0x04, 0xEB,
157 | 0x76, 0x6D, 0x6E, 0x12, 0x05, 0xCE, 0x33, 0x77, 0xC4, 0xB1, 0x26, 0x03, 0xF9, 0xB2, 0xCA, 0x09,
158 | 0xD4, 0xC6, 0xBE, 0x12, 0xA4, 0x3E, 0x52, 0x25, 0xA8, 0x61, 0x5A, 0xD0, 0x76, 0xC0, 0x35, 0x5F,
159 | 0x26, 0x51, 0x4C, 0xC6, 0xB2, 0x07, 0x83, 0x35, 0x74, 0x0F, 0xA4, 0x66, 0x6D, 0x34, 0x91, 0x60,
160 | 0xA9, 0x73, 0x29, 0xFC, 0x66, 0xD9, 0xC2, 0x70, 0x4B, 0x57, 0xC9, 0xB0, 0xBD, 0xF4, 0xA5, 0x35,
161 | 0x59, 0x83, 0xE0, 0x0B, 0x6C, 0x62, 0xE0, 0x1E, 0x68, 0x64, 0xF2, 0x7B, 0x00, 0x77, 0x6B, 0xB6,
162 | 0xA3, 0x3D, 0xD6, 0x8E, 0x6A, 0x35, 0x53, 0x55, 0xE9, 0xAE, 0x0B, 0x6D, 0x4E, 0x74, 0x23, 0x0B,
163 | 0x4B, 0x10, 0xAA, 0x9A, 0x59, 0x0C, 0x38, 0x1B, 0x81, 0xAA, 0xBA, 0xC0, 0x11, 0xD6, 0x98, 0x66,
164 | 0xA9, 0x23, 0xF1, 0x97, 0x1D, 0xC9, 0x13, 0xB5, 0x07, 0x95, 0xF5, 0x05, 0xD4, 0x31, 0xAB, 0x25,
165 | 0x86, 0x30, 0xD3, 0x29, 0x13, 0xDE, 0x04, 0x03, 0x90, 0x07, 0x5A, 0xD5, 0x05, 0x14, 0xB5, 0x8E,
166 | 0x1C, 0x4D, 0x44, 0xB8, 0x1C, 0x05, 0xF9, 0xF0, 0x6B, 0x9A, 0x0F, 0xBC, 0xB4, 0x18, 0xDD, 0x97,
167 | 0x80, 0x50, 0xD2, 0xE6, 0xE0, 0x88, 0x8F, 0xF2, 0x21, 0xF4, 0xB2, 0x05, 0x9D, 0x02, 0x58, 0xFC,
168 | 0xC6, 0x71, 0x3E, 0x8A, 0x27, 0xC5, 0x68, 0x42, 0xEF, 0x17, 0x78, 0x51, 0x01, 0xF5, 0xA9, 0xEE,
169 | 0x28, 0x1B, 0xDB, 0x68, 0xCE, 0xF3, 0x41, 0x6B, 0x29, 0x7F, 0xF0, 0xFF, 0x28, 0x7F, 0xCC, 0xC7,
170 | 0x85, 0x34, 0x71, 0x31, 0x1A, 0xB3, 0x42, 0x96, 0x61, 0x18, 0xFF, 0x90, 0x93, 0xA4, 0xD4, 0x13,
171 | 0x97, 0x7A, 0x5A, 0xF1, 0xB3, 0xB6, 0x53, 0x98, 0x8E, 0x31, 0xAA, 0xF8, 0xE3, 0xC8, 0xF6, 0xF0,
172 | 0xF7, 0x3C, 0xF2, 0x65, 0x6D, 0x69, 0x5A, 0xA1, 0x31, 0x82, 0x3A, 0x57, 0x37, 0xCB, 0x7E, 0x9A,
173 | 0xFD, 0xB7, 0xAD, 0xE8, 0xD1, 0xF1, 0xE9, 0x71, 0xFF, 0xB8, 0x5C, 0x38, 0x23, 0xE7, 0x25, 0x93,
174 | 0x8A, 0x2B, 0x5D, 0xFA, 0xB2, 0x22, 0x80, 0x02, 0x1B, 0x45, 0x01, 0x7B, 0xDD, 0xDC, 0x54, 0x7E,
175 | 0xF1, 0xB6, 0x77, 0x71, 0x6E, 0xC7, 0x24, 0x01, 0x8F, 0x24, 0x15, 0xE6, 0xC2, 0x82, 0x44, 0xF9,
176 | 0xE0, 0xD7, 0xC7, 0xA5, 0x72, 0x5D, 0x7E, 0x61, 0x70, 0xC4, 0xDC, 0x52, 0xA7, 0xA9, 0x7E, 0x78,
177 | 0xE2, 0x62, 0x5D, 0x99, 0xBF, 0x04, 0x41, 0x72, 0x1A, 0x2D, 0x13, 0x55, 0x11, 0x67, 0x46, 0xE5,
178 | 0x30, 0x2F, 0xEE, 0xB2, 0x75, 0x0D, 0xD3, 0xC8, 0xB4, 0xC4, 0x84, 0xA5, 0xE5, 0x46, 0xA5, 0x12,
179 | 0x14, 0xFE, 0xA2, 0xB6, 0xE7, 0x8B, 0x91, 0x24, 0xB7, 0x5A, 0x73, 0xAB, 0x6F, 0x41, 0x2A, 0x3E,
180 | 0x58, 0x04, 0x23, 0x66, 0x39, 0xDB, 0x16, 0x77, 0xA3, 0x43, 0xEE, 0x61, 0x5C, 0x7F, 0xBA, 0x35,
181 | 0x78, 0xD2, 0x3C, 0x79, 0x61, 0x9E, 0xFC, 0xB1, 0x7B, 0x2E, 0x1C, 0x45, 0xF9, 0xDA, 0xE2, 0x98,
182 | 0xF6, 0x10, 0x58, 0xBB, 0x6D, 0x2F, 0x7D, 0x18, 0x20, 0xD2, 0x83, 0xCB, 0x00, 0xF4, 0x63, 0x58,
183 | 0xFF, 0x4A, 0xEE, 0x88, 0x7A, 0x09, 0xAA, 0xA2, 0xAD, 0x73, 0x54, 0xD8, 0xEE, 0xFD, 0x81, 0xA3,
184 | 0xF2, 0xCE, 0x65, 0x18, 0x48, 0x97, 0xC3, 0x92, 0x37, 0x8B, 0x75, 0xC1, 0x61, 0x19, 0x31, 0x64,
185 | 0x6C, 0x00, 0xE3, 0xCD, 0x5D, 0x49, 0x13, 0xD5, 0x1C, 0xB4, 0xF0, 0x1B, 0x08, 0x8A, 0x4F, 0x39,
186 | 0xCE, 0x9A, 0x38, 0xAD, 0x62, 0x72, 0xC5, 0x23, 0xC8, 0x4A, 0x67, 0x89, 0xC0, 0x6E, 0x10, 0x0D,
187 | 0x0D, 0x7C, 0x64, 0x9A, 0xA1, 0xB6, 0x1D, 0x3E, 0x37, 0xD7, 0xBC, 0xD9, 0x54, 0xFA, 0x4B, 0x62,
188 | 0x79, 0xD5, 0xB0, 0x8B, 0x1C, 0x56, 0xCC, 0x75, 0x7D, 0x1F, 0xF4, 0xA3, 0x4E, 0x29, 0xAF, 0x48,
189 | 0xA4, 0x53, 0xD1, 0x83, 0xC4, 0x86, 0xA2, 0x41, 0xBE, 0x91, 0x40, 0x44, 0x72, 0x4A, 0x33, 0x5D,
190 | 0xC7, 0xCA, 0xD2, 0x0B, 0x28, 0x49, 0x7A, 0xB2, 0x73, 0x95, 0x49, 0x6B, 0x25, 0x06, 0xFE, 0xC8,
191 | 0xD7, 0xF0, 0xC7, 0xA1, 0xD0, 0xA3, 0x83, 0x9B, 0x49, 0x2B, 0x83, 0xA4, 0x23, 0x64, 0x83, 0xA9,
192 | 0x37, 0xE4, 0xBB, 0xA8, 0x2D, 0x2F, 0xCB, 0xB4, 0x16, 0x50, 0x70, 0x71, 0x83, 0xBB, 0x11, 0x30,
193 | 0x52, 0x5A, 0xC4, 0x9E, 0x94, 0xA8, 0xC7, 0x8F, 0x10, 0x1F, 0x53, 0x4A, 0x20, 0x06, 0x20, 0xA6,
194 | 0x40, 0xD0, 0xA7, 0x42, 0x8A, 0x54, 0xE6, 0x92, 0x53, 0x2A, 0x20, 0xCA, 0x48, 0xCD, 0xE2, 0xC1,
195 | 0x85, 0x78, 0xD4, 0x46, 0xD6, 0x80, 0xFD, 0xDC, 0xBD, 0x73, 0x33, 0xDE, 0x90, 0x68, 0x09, 0x56,
196 | 0x36, 0x3D, 0x9A, 0xA6, 0x52, 0x5C, 0x54, 0xC7, 0x19, 0xF8, 0xA8, 0xA1, 0x03, 0x5A, 0x23, 0x84,
197 | 0x11, 0x1E, 0x84, 0x8A, 0x01, 0x40, 0x7F, 0x42, 0xC3, 0x1C, 0x22, 0x70, 0x08, 0x20, 0x82, 0xA0,
198 | 0x7F, 0x49, 0x0D, 0xF7, 0x64, 0x05, 0xC9, 0xF8, 0xD8, 0x6D, 0x35, 0xF0, 0x9D, 0x66, 0x95, 0xEC,
199 | 0x20, 0xA5, 0xBD, 0x68, 0x24, 0xFA, 0x64, 0x98, 0x1A, 0x50, 0x00, 0xAC, 0xD9, 0x01, 0xA0, 0x1E,
200 | 0x24, 0x5E, 0x63, 0x2B, 0x3F, 0xEF, 0x04, 0x2A, 0xBB, 0x00, 0xAB, 0xBB, 0x8E, 0x87, 0x5F, 0x39,
201 | 0x4F, 0x19, 0xA7, 0x39, 0x26, 0x00, 0x7B, 0x93, 0x68, 0x7A, 0x99, 0x30, 0x2E, 0xCE, 0x64, 0x1B,
202 | 0x6A, 0x6C, 0xB4, 0xE4, 0xF5, 0xA9, 0x87, 0x15, 0x79, 0x3F, 0xC5, 0x8B, 0xCB, 0x0C, 0xF3, 0xBA,
203 | 0x53, 0x79, 0x77, 0xB1, 0x86, 0x70, 0x21, 0x50, 0x66, 0x38, 0xB3, 0x29, 0x74, 0xB0, 0xFA, 0xA1,
204 | 0x48, 0x82, 0x7A, 0x4F, 0xB7, 0x42, 0xE2, 0xC1, 0x44, 0xED, 0x81, 0xF9, 0xDC, 0xC2, 0xD8, 0xE1,
205 | 0x94, 0x83, 0x5A, 0x0A, 0xB5, 0x02, 0x45, 0xC6, 0x95, 0xCD, 0x98, 0x35, 0x1D, 0x6A, 0x58, 0x88,
206 | 0x61, 0xE0, 0xAF, 0xFE, 0x05, 0x0F, 0x1E, 0x1C, 0xC8, 0x55, 0x3F, 0xE1, 0x23, 0xE3, 0x7E, 0xF4,
207 | 0x23, 0x3E, 0x3E, 0xAF, 0xF0, 0xF1, 0x79, 0x1D, 0x1F, 0xB4, 0xAA, 0x3C, 0x98, 0x0C, 0x80, 0xEC,
208 | 0x19, 0xE1, 0x64, 0x4C, 0x13, 0x58, 0xC0, 0x43, 0x50, 0x25, 0x7F, 0x8B, 0xB3, 0x84, 0xFE, 0x98,
209 | 0xB3, 0xDE, 0x84, 0x8D, 0xC4, 0x23, 0xFE, 0x8A, 0xD5, 0xFF, 0x82, 0x4B, 0x3C, 0x70, 0x3D, 0x97,
210 | 0x79, 0x6D, 0x5A, 0x49, 0x28, 0x3F, 0x7E, 0x2B, 0x91, 0x7E, 0xE4, 0x42, 0x78, 0xA9, 0x38, 0xC8,
211 | 0xDF, 0xB7, 0xF4, 0x00, 0xBC, 0x11, 0xF8, 0x29, 0x35, 0x75, 0xBC, 0x0B, 0xA5, 0xFC, 0x29, 0x30,
212 | 0x64, 0xA8, 0xC0, 0x47, 0xDD, 0xD9, 0xDC, 0x12, 0xAE, 0x01, 0x8A, 0xF1, 0xA3, 0x29, 0xB0, 0xEA,
213 | 0xC9, 0x02, 0xD7, 0x9E, 0x40, 0x26, 0x04, 0x91, 0xE0, 0x48, 0xC8, 0xA7, 0x8D, 0x2F, 0x07, 0x9B,
214 | 0x37, 0x35, 0xC8, 0x43, 0x2E, 0xFC, 0x98, 0x2E, 0x0C, 0x36, 0x6F, 0xFE, 0x6D, 0x36, 0xC6, 0xCC,
215 | 0x5A, 0x76, 0xA4, 0x96, 0x4C, 0xF6, 0xF4, 0x0B, 0xBF, 0x71, 0x09, 0x48, 0x5D, 0x49, 0x78, 0x45,
216 | 0x34, 0x03, 0x6B, 0x43, 0x61, 0xE1, 0x07, 0xFF, 0x47, 0x09, 0xF8, 0x91, 0x9E, 0x07, 0xCE, 0xBD,
217 | 0xE6, 0x3D, 0x5E, 0x2F, 0x3E, 0x85, 0xE9, 0x56, 0xE9, 0xC1, 0x4A, 0xC7, 0xEF, 0x53, 0x3A, 0x76,
218 | 0x59, 0xA2, 0x14, 0x4A, 0x14, 0x59, 0x88, 0x1A, 0x6A, 0x50, 0x0E, 0x51, 0x98, 0x89, 0x17, 0xCD,
219 | 0x81, 0x02, 0x9B, 0x73, 0x34, 0x5B, 0x3A, 0x02, 0x0F, 0xF4, 0xF5, 0x45, 0xEE, 0xFC, 0x74, 0x76,
220 | 0x7A, 0x22, 0x44, 0x7C, 0xA5, 0x62, 0x22, 0xD0, 0xAA, 0x2E, 0x2C, 0x2F, 0xCF, 0x9C, 0x89, 0xE4,
221 | 0xA1, 0x28, 0x75, 0x30, 0x31, 0x28, 0x87, 0xFE, 0x74, 0x31, 0xFC, 0x0A, 0x71, 0xD6, 0xD0, 0xCF,
222 | 0x52, 0x48, 0x58, 0x5B, 0x36, 0xA2, 0xF7, 0xFB, 0x97, 0xF6, 0xAE, 0xDD, 0x84, 0xBA, 0x00, 0xB4,
223 | 0x0A, 0x69, 0x19, 0xEE, 0x7D, 0xFE, 0xB7, 0x90, 0xB7, 0xFF, 0x1E, 0x32, 0x83, 0xA8, 0x95, 0x42,
224 | 0x58, 0x2A, 0xF0, 0xAB, 0xB8, 0x93, 0x24, 0x9A, 0x4A, 0xB4, 0xE3, 0x24, 0xC1, 0x4B, 0xE9, 0x43,
225 | 0x85, 0xA2, 0x0D, 0x61, 0x31, 0xA5, 0x89, 0xE6, 0x47, 0x34, 0xD5, 0x78, 0x24, 0xB4, 0x34, 0x8B,
226 | 0x63, 0x68, 0x5C, 0x56, 0xF4, 0x61, 0xEB, 0xC5, 0xEB, 0xCB, 0xFB, 0x8C, 0x66, 0xD4, 0xCF, 0x97,
227 | 0x69, 0x52, 0xD1, 0x0B, 0x56, 0x50, 0xDF, 0x10, 0xEE, 0x7E, 0xB9, 0xC9, 0xEB, 0xA9, 0x8C, 0x73,
228 | 0x8C, 0xA2, 0x1B, 0x2D, 0x35, 0x07, 0xE9, 0x26, 0x40, 0xD5, 0xE5, 0x59, 0x10, 0xCC, 0xDB, 0x2B,
229 | 0xB4, 0xA0, 0xF1, 0x8A, 0x44, 0x24, 0x9F, 0xCB, 0x67, 0x7F, 0xE4, 0xC9, 0xA9, 0xE2, 0x82, 0x50,
230 | 0xF2, 0x54, 0xA9, 0x36, 0xAD, 0x0D, 0x63, 0x83, 0x6A, 0x8C, 0xA7, 0x82, 0x70, 0x0F, 0xAF, 0x51,
231 | 0xE9, 0xC2, 0x2C, 0x6A, 0x29, 0xDC, 0xDE, 0x46, 0x5F, 0xCB, 0x6D, 0xE9, 0x89, 0x7C, 0x2A, 0x25,
232 | 0xE3, 0xAE, 0xAE, 0x63, 0x55, 0x45, 0xB1, 0x3E, 0x25, 0x61, 0x5A, 0x26, 0x5B, 0x54, 0x06, 0x26,
233 | 0x77, 0x0B, 0x70, 0x9B, 0x06, 0x29, 0x1C, 0xBD, 0x7E, 0x7F, 0xCE, 0x46, 0xD1, 0xCE, 0x11, 0x80,
234 | 0x69, 0xC5, 0x3E, 0x93, 0xD7, 0xE0, 0x24, 0xCC, 0x73, 0x07, 0x32, 0xE9, 0x4A, 0x03, 0x0E, 0xA9,
235 | 0x98, 0x44, 0xFE, 0x81, 0x7E, 0xA0, 0x3B, 0x3A, 0xFC, 0xBB, 0x09, 0x35, 0x47, 0xCD, 0xA5, 0xD0,
236 | 0xA4, 0xFA, 0x74, 0x70, 0xF5, 0x06, 0xC2, 0x53, 0x0C, 0xA5, 0x01, 0x17, 0x50, 0x34, 0xD7, 0x74,
237 | 0x7C, 0x7A, 0x7D, 0x0C, 0x29, 0xC8, 0x7F, 0x21, 0x37, 0x66, 0xBB, 0xAA, 0x6C, 0xB8, 0xF3, 0xEA,
238 | 0x75, 0x56, 0x2E, 0x03, 0x7A, 0x61, 0x8C, 0x58, 0x0F, 0x29, 0x7E, 0xFB, 0x7B, 0xF4, 0x9E, 0x8D,
239 | 0x15, 0xD2, 0x6A, 0x5D, 0x6F, 0xCE, 0x76, 0x90, 0x67, 0x89, 0xD5, 0x43, 0x2C, 0x70, 0x97, 0x1F,
240 | 0x29, 0x59, 0x95, 0x35, 0xDC, 0xF6, 0x48, 0x10, 0xE0, 0xC7, 0x5A, 0x03, 0x1B, 0x6A, 0x22, 0xB2,
241 | 0xD4, 0x42, 0x22, 0x29, 0x08, 0x90, 0xD2, 0x3E, 0x84, 0x39, 0xD3, 0x92, 0x65, 0x86, 0xB2, 0xA1,
242 | 0xBC, 0xFF, 0xC5, 0x9A, 0xA3, 0x64, 0x46, 0xE8, 0xCE, 0xF9, 0x6C, 0x73, 0x53, 0xD8, 0x85, 0x99,
243 | 0x18, 0x05, 0x52, 0x8A, 0x01, 0x1C, 0x9A, 0x7D, 0x68, 0x2D, 0x8C, 0xB2, 0x90, 0x58, 0xAB, 0x3D,
244 | 0xD2, 0xB6, 0x51, 0x55, 0x03, 0x54, 0x7C, 0x46, 0x01, 0x03, 0xCE, 0xB2, 0x24, 0x80, 0xA8, 0x8B,
245 | 0x39, 0xBA, 0xB2, 0x2D, 0xC5, 0xBA, 0xD0, 0x84, 0x0E, 0xEC, 0x67, 0xC8, 0x12, 0x95, 0x97, 0xAD,
246 | 0xA2, 0x27, 0x12, 0xC5, 0x77, 0x95, 0x9E, 0xC8, 0x6F, 0xE5, 0x84, 0xAA, 0xC8, 0x77, 0x88, 0x2F,
247 | 0x13, 0x5C, 0xD4, 0xD1, 0x13, 0xA0, 0x24, 0x83, 0x52, 0x34, 0x60, 0x2A, 0x2C, 0x37, 0xEE, 0xEB,
248 | 0xD3, 0xE9, 0xB4, 0x8E, 0xDF, 0x6A, 0xEB, 0x70, 0x82, 0xB2, 0x02, 0x5F, 0x5F, 0xC7, 0x21, 0x47,
249 | 0x15, 0x58, 0xF8, 0x6E, 0xE1, 0xAC, 0xBA, 0xE8, 0x42, 0x7F, 0x2B, 0xDE, 0xD4, 0xAA, 0xD2, 0x59,
250 | 0xE1, 0x73, 0x79, 0xDB, 0x7B, 0x3B, 0x2B, 0x20, 0x32, 0xC4, 0xAF, 0xB2, 0x90, 0x69, 0x20, 0x0D,
251 | 0x3B, 0xE5, 0x46, 0x56, 0x25, 0x85, 0x65, 0x5C, 0xB0, 0xE3, 0x2C, 0x9D, 0x18, 0x33, 0x60, 0xDD,
252 | 0x11, 0x96, 0xD2, 0x95, 0x43, 0x2D, 0x65, 0xB7, 0x0E, 0xB7, 0x0A, 0xFB, 0x70, 0x30, 0x83, 0x94,
253 | 0x79, 0xFB, 0xF3, 0x4F, 0x39, 0x5B, 0xDE, 0xF6, 0x92, 0x62, 0x71, 0xE1, 0xF3, 0xFC, 0xA9, 0x35,
254 | 0xAF, 0x69, 0xA5, 0xD1, 0xAF, 0xC4, 0x97, 0xBD, 0x46, 0xFE, 0x19, 0x3B, 0xFF, 0x9C, 0xAD, 0x81,
255 | 0xB1, 0x43, 0x23, 0x2A, 0xDC, 0x4C, 0x8C, 0xEA, 0x2F, 0x34, 0xE6, 0x63, 0x79, 0x29, 0xBF, 0x2D,
256 | 0xA0, 0x54, 0xA9, 0xD3, 0x68, 0x78, 0x3E, 0xFF, 0x9A, 0x42, 0x19, 0x1D, 0x65, 0xFE, 0x28, 0x20,
257 | 0x09, 0xC5, 0x82, 0xA3, 0x41, 0xBE, 0x92, 0xFB, 0x46, 0xC0, 0x86, 0x69, 0x03, 0x93, 0x6D, 0xCB,
258 | 0xDE, 0xB2, 0x77, 0x71, 0x64, 0x7F, 0x4D, 0xF7, 0x57, 0x4F, 0xD8, 0x5F, 0x34, 0x69, 0x58, 0x0B,
259 | 0xE7, 0xB5, 0xAB, 0x8A, 0x4D, 0x6A, 0x83, 0xFB, 0xC4, 0xA7, 0x70, 0x3D, 0x6F, 0xB3, 0xCC, 0xB6,
260 | 0x1A, 0xE4, 0x5F, 0x60, 0xD4, 0x31, 0xBA, 0x95, 0x2F, 0x92, 0xF4, 0x81, 0x7B, 0x18, 0x5B, 0x17,
261 | 0x54, 0x26, 0x70, 0x49, 0xD5, 0x87, 0x34, 0xB9, 0xD3, 0x9C, 0x2F, 0x39, 0xC3, 0xB7, 0x3C, 0xA8,
262 | 0x03, 0xE4, 0x37, 0x9C, 0x72, 0x39, 0xB0, 0xBF, 0x07, 0x5D, 0x33, 0x2A, 0x41, 0x79, 0xB1, 0x26,
263 | 0x9B, 0xE6, 0x7C, 0x02, 0x82, 0x01, 0x70, 0xB1, 0xA3, 0x48, 0xCD, 0x2B, 0xCB, 0x98, 0x9B, 0x57,
264 | 0x96, 0x54, 0xE2, 0x5F, 0x59, 0xCC, 0xDB, 0x9F, 0xFC, 0xDB, 0x4C, 0xF9, 0x7F, 0x5B, 0x28, 0x36,
265 | 0x32, 0xF9, 0xE1, 0x09, 0xF7, 0x56, 0x3F, 0x45, 0xAD, 0x47, 0x51, 0xBB, 0xF7, 0xFF, 0x17, 0x53,
266 | 0xE8, 0x9D, 0x36, 0x92, 0x29, 0x00, 0x00
267 | };
268 |
269 | #define SPIFFS_MAXLENGTH_FILEPATH 32
270 | const char *excludeListFile = "/.exclude.files";
271 |
272 | typedef struct ExcludeListS {
273 | char *item;
274 | ExcludeListS *next;
275 | } ExcludeList;
276 |
277 | static ExcludeList *excludes = NULL;
278 |
279 | static bool matchWild(const char *pattern, const char *testee) {
280 | const char *nxPat = NULL, *nxTst = NULL;
281 |
282 | while (*testee) {
283 | if (( *pattern == '?' ) || (*pattern == *testee)){
284 | pattern++;testee++;
285 | continue;
286 | }
287 | if (*pattern=='*'){
288 | nxPat=pattern++; nxTst=testee;
289 | continue;
290 | }
291 | if (nxPat){
292 | pattern = nxPat+1; testee=++nxTst;
293 | continue;
294 | }
295 | return false;
296 | }
297 | while (*pattern=='*'){pattern++;}
298 | return (*pattern == 0);
299 | }
300 |
301 | static bool addExclude(const char *item){
302 | size_t len = strlen(item);
303 | if(!len){
304 | return false;
305 | }
306 | ExcludeList *e = (ExcludeList *)malloc(sizeof(ExcludeList));
307 | if(!e){
308 | return false;
309 | }
310 | e->item = (char *)malloc(len+1);
311 | if(!e->item){
312 | free(e);
313 | return false;
314 | }
315 | memcpy(e->item, item, len+1);
316 | e->next = excludes;
317 | excludes = e;
318 | return true;
319 | }
320 |
321 | static void loadExcludeList(fs::FS &_fs, const char *filename){
322 | static char linebuf[SPIFFS_MAXLENGTH_FILEPATH];
323 | fs::File excludeFile=_fs.open(filename, "r");
324 | if(!excludeFile){
325 | //addExclude("/*.js.gz");
326 | return;
327 | }
328 | #ifdef ESP32
329 | if(excludeFile.isDirectory()){
330 | excludeFile.close();
331 | return;
332 | }
333 | #endif
334 | if (excludeFile.size() > 0){
335 | uint8_t idx;
336 | bool isOverflowed = false;
337 | while (excludeFile.available()){
338 | linebuf[0] = '\0';
339 | idx = 0;
340 | int lastChar;
341 | do {
342 | lastChar = excludeFile.read();
343 | if(lastChar != '\r'){
344 | linebuf[idx++] = (char) lastChar;
345 | }
346 | } while ((lastChar >= 0) && (lastChar != '\n') && (idx < SPIFFS_MAXLENGTH_FILEPATH));
347 |
348 | if(isOverflowed){
349 | isOverflowed = (lastChar != '\n');
350 | continue;
351 | }
352 | isOverflowed = (idx >= SPIFFS_MAXLENGTH_FILEPATH);
353 | linebuf[idx-1] = '\0';
354 | if(!addExclude(linebuf)){
355 | excludeFile.close();
356 | return;
357 | }
358 | }
359 | }
360 | excludeFile.close();
361 | }
362 |
363 | static bool isExcluded(fs::FS &_fs, const char *filename) {
364 | if(excludes == NULL){
365 | loadExcludeList(_fs, excludeListFile);
366 | }
367 | ExcludeList *e = excludes;
368 | while(e){
369 | if (matchWild(e->item, filename)){
370 | return true;
371 | }
372 | e = e->next;
373 | }
374 | return false;
375 | }
376 |
377 | // WEB HANDLER IMPLEMENTATION
378 |
379 | #ifdef ESP32
380 | SPIFFSEditor::SPIFFSEditor(const fs::FS& fs, const String& username, const String& password)
381 | #else
382 | SPIFFSEditor::SPIFFSEditor(const String& username, const String& password, const fs::FS& fs)
383 | #endif
384 | :_fs(fs)
385 | ,_username(username)
386 | ,_password(password)
387 | ,_authenticated(false)
388 | ,_startTime(0)
389 | {}
390 |
391 | bool SPIFFSEditor::canHandle(AsyncWebServerRequest *request){
392 | if(request->url().equalsIgnoreCase("/edit")){
393 | if(request->method() == HTTP_GET){
394 | if(request->hasParam("list"))
395 | return true;
396 | if(request->hasParam("edit")){
397 | request->_tempFile = _fs.open(request->arg("edit"), "r");
398 | if(!request->_tempFile){
399 | return false;
400 | }
401 | #ifdef ESP32
402 | if(request->_tempFile.isDirectory()){
403 | request->_tempFile.close();
404 | return false;
405 | }
406 | #endif
407 | }
408 | if(request->hasParam("download")){
409 | request->_tempFile = _fs.open(request->arg("download"), "r");
410 | if(!request->_tempFile){
411 | return false;
412 | }
413 | #ifdef ESP32
414 | if(request->_tempFile.isDirectory()){
415 | request->_tempFile.close();
416 | return false;
417 | }
418 | #endif
419 | }
420 | request->addInterestingHeader("If-Modified-Since");
421 | return true;
422 | }
423 | else if(request->method() == HTTP_POST)
424 | return true;
425 | else if(request->method() == HTTP_DELETE)
426 | return true;
427 | else if(request->method() == HTTP_PUT)
428 | return true;
429 |
430 | }
431 | return false;
432 | }
433 |
434 |
435 | void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request){
436 | if(_username.length() && _password.length() && !request->authenticate(_username.c_str(), _password.c_str()))
437 | return request->requestAuthentication();
438 |
439 | if(request->method() == HTTP_GET){
440 | if(request->hasParam("list")){
441 | String path = request->getParam("list")->value();
442 | #ifdef ESP32
443 | File dir = _fs.open(path);
444 | #else
445 | Dir dir = _fs.openDir(path);
446 | #endif
447 | path = String();
448 | String output = "[";
449 | #ifdef ESP32
450 | File entry = dir.openNextFile();
451 | while(entry){
452 | #else
453 | while(dir.next()){
454 | fs::File entry = dir.openFile("r");
455 | #endif
456 | if (isExcluded(_fs, entry.name())) {
457 | #ifdef ESP32
458 | entry = dir.openNextFile();
459 | #endif
460 | continue;
461 | }
462 | if (output != "[") output += ',';
463 | output += "{\"type\":\"";
464 | output += "file";
465 | output += "\",\"name\":\"";
466 | output += String(entry.name());
467 | output += "\",\"size\":";
468 | output += String(entry.size());
469 | output += "}";
470 | #ifdef ESP32
471 | entry = dir.openNextFile();
472 | #else
473 | entry.close();
474 | #endif
475 | }
476 | #ifdef ESP32
477 | dir.close();
478 | #endif
479 | output += "]";
480 | request->send(200, "application/json", output);
481 | output = String();
482 | }
483 | else if(request->hasParam("edit") || request->hasParam("download")){
484 | request->send(request->_tempFile, request->_tempFile.name(), String(), request->hasParam("download"));
485 | }
486 | else {
487 | const char * buildTime = __DATE__ " " __TIME__ " GMT";
488 | if (request->header("If-Modified-Since").equals(buildTime)) {
489 | request->send(304);
490 | } else {
491 | AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", edit_htm_gz, edit_htm_gz_len);
492 | response->addHeader("Content-Encoding", "gzip");
493 | response->addHeader("Last-Modified", buildTime);
494 | request->send(response);
495 | }
496 | }
497 | } else if(request->method() == HTTP_DELETE){
498 | if(request->hasParam("path", true)){
499 | _fs.remove(request->getParam("path", true)->value());
500 | request->send(200, "", "DELETE: "+request->getParam("path", true)->value());
501 | } else
502 | request->send(404);
503 | } else if(request->method() == HTTP_POST){
504 | if(request->hasParam("data", true, true) && _fs.exists(request->getParam("data", true, true)->value()))
505 | request->send(200, "", "UPLOADED: "+request->getParam("data", true, true)->value());
506 | else
507 | request->send(500);
508 | } else if(request->method() == HTTP_PUT){
509 | if(request->hasParam("path", true)){
510 | String filename = request->getParam("path", true)->value();
511 | if(_fs.exists(filename)){
512 | request->send(200);
513 | } else {
514 | fs::File f = _fs.open(filename, "w");
515 | if(f){
516 | f.write((uint8_t)0x00);
517 | f.close();
518 | request->send(200, "", "CREATE: "+filename);
519 | } else {
520 | request->send(500);
521 | }
522 | }
523 | } else
524 | request->send(400);
525 | }
526 | }
527 |
528 | void SPIFFSEditor::handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){
529 | if(!index){
530 | if(!_username.length() || request->authenticate(_username.c_str(),_password.c_str())){
531 | _authenticated = true;
532 | request->_tempFile = _fs.open(filename, "w");
533 | _startTime = millis();
534 | }
535 | }
536 | if(_authenticated && request->_tempFile){
537 | if(len){
538 | request->_tempFile.write(data,len);
539 | }
540 | if(final){
541 | request->_tempFile.close();
542 | }
543 | }
544 | }
545 |
--------------------------------------------------------------------------------
/src/SPIFFSEditor.h:
--------------------------------------------------------------------------------
1 | #ifndef SPIFFSEditor_H_
2 | #define SPIFFSEditor_H_
3 | #include
4 |
5 | class SPIFFSEditor: public AsyncWebHandler {
6 | private:
7 | fs::FS _fs;
8 | String _username;
9 | String _password;
10 | bool _authenticated;
11 | uint32_t _startTime;
12 | public:
13 | #ifdef ESP32
14 | SPIFFSEditor(const fs::FS& fs, const String& username=String(), const String& password=String());
15 | #else
16 | SPIFFSEditor(const String& username=String(), const String& password=String(), const fs::FS& fs=SPIFFS);
17 | #endif
18 | virtual bool canHandle(AsyncWebServerRequest *request) override final;
19 | virtual void handleRequest(AsyncWebServerRequest *request) override final;
20 | virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final;
21 | virtual bool isRequestHandlerTrivial() override final {return false;}
22 | };
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/src/StringArray.h:
--------------------------------------------------------------------------------
1 | /*
2 | Asynchronous WebServer library for Espressif MCUs
3 |
4 | Copyright (c) 2016 Hristo Gochkov. All rights reserved.
5 | This file is part of the esp8266 core for Arduino environment.
6 |
7 | This library is free software; you can redistribute it and/or
8 | modify it under the terms of the GNU Lesser General Public
9 | License as published by the Free Software Foundation; either
10 | version 2.1 of the License, or (at your option) any later version.
11 |
12 | This library is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | Lesser General Public License for more details.
16 |
17 | You should have received a copy of the GNU Lesser General Public
18 | License along with this library; if not, write to the Free Software
19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 | */
21 | #ifndef STRINGARRAY_H_
22 | #define STRINGARRAY_H_
23 |
24 | #include "stddef.h"
25 | #include "WString.h"
26 |
27 | template
28 | class LinkedListNode {
29 | T _value;
30 | public:
31 | LinkedListNode* next;
32 | LinkedListNode(const T val): _value(val), next(nullptr) {}
33 | ~LinkedListNode(){}
34 | const T& value() const { return _value; };
35 | T& value(){ return _value; }
36 | };
37 |
38 | template class Item = LinkedListNode>
39 | class LinkedList {
40 | public:
41 | typedef Item ItemType;
42 | typedef std::function OnRemove;
43 | typedef std::function Predicate;
44 | private:
45 | ItemType* _root;
46 | OnRemove _onRemove;
47 |
48 | class Iterator {
49 | ItemType* _node;
50 | public:
51 | Iterator(ItemType* current = nullptr) : _node(current) {}
52 | Iterator(const Iterator& i) : _node(i._node) {}
53 | Iterator& operator ++() { _node = _node->next; return *this; }
54 | bool operator != (const Iterator& i) const { return _node != i._node; }
55 | const T& operator * () const { return _node->value(); }
56 | const T* operator -> () const { return &_node->value(); }
57 | };
58 |
59 | public:
60 | typedef const Iterator ConstIterator;
61 | ConstIterator begin() const { return ConstIterator(_root); }
62 | ConstIterator end() const { return ConstIterator(nullptr); }
63 |
64 | LinkedList(OnRemove onRemove) : _root(nullptr), _onRemove(onRemove) {}
65 | ~LinkedList(){}
66 | void add(const T& t){
67 | auto it = new ItemType(t);
68 | if(!_root){
69 | _root = it;
70 | } else {
71 | auto i = _root;
72 | while(i->next) i = i->next;
73 | i->next = it;
74 | }
75 | }
76 | T& front() const {
77 | return _root->value();
78 | }
79 |
80 | bool isEmpty() const {
81 | return _root == nullptr;
82 | }
83 | size_t length() const {
84 | size_t i = 0;
85 | auto it = _root;
86 | while(it){
87 | i++;
88 | it = it->next;
89 | }
90 | return i;
91 | }
92 | size_t count_if(Predicate predicate) const {
93 | size_t i = 0;
94 | auto it = _root;
95 | while(it){
96 | if (!predicate){
97 | i++;
98 | }
99 | else if (predicate(it->value())) {
100 | i++;
101 | }
102 | it = it->next;
103 | }
104 | return i;
105 | }
106 | const T* nth(size_t N) const {
107 | size_t i = 0;
108 | auto it = _root;
109 | while(it){
110 | if(i++ == N)
111 | return &(it->value());
112 | it = it->next;
113 | }
114 | return nullptr;
115 | }
116 | bool remove(const T& t){
117 | auto it = _root;
118 | auto pit = _root;
119 | while(it){
120 | if(it->value() == t){
121 | if(it == _root){
122 | _root = _root->next;
123 | } else {
124 | pit->next = it->next;
125 | }
126 |
127 | if (_onRemove) {
128 | _onRemove(it->value());
129 | }
130 |
131 | delete it;
132 | return true;
133 | }
134 | pit = it;
135 | it = it->next;
136 | }
137 | return false;
138 | }
139 | bool remove_first(Predicate predicate){
140 | auto it = _root;
141 | auto pit = _root;
142 | while(it){
143 | if(predicate(it->value())){
144 | if(it == _root){
145 | _root = _root->next;
146 | } else {
147 | pit->next = it->next;
148 | }
149 | if (_onRemove) {
150 | _onRemove(it->value());
151 | }
152 | delete it;
153 | return true;
154 | }
155 | pit = it;
156 | it = it->next;
157 | }
158 | return false;
159 | }
160 |
161 | void free(){
162 | while(_root != nullptr){
163 | auto it = _root;
164 | _root = _root->next;
165 | if (_onRemove) {
166 | _onRemove(it->value());
167 | }
168 | delete it;
169 | }
170 | _root = nullptr;
171 | }
172 | };
173 |
174 |
175 | class StringArray : public LinkedList {
176 | public:
177 |
178 | StringArray() : LinkedList(nullptr) {}
179 |
180 | bool containsIgnoreCase(const String& str){
181 | for (const auto& s : *this) {
182 | if (str.equalsIgnoreCase(s)) {
183 | return true;
184 | }
185 | }
186 | return false;
187 | }
188 | };
189 |
190 |
191 |
192 |
193 | #endif /* STRINGARRAY_H_ */
194 |
--------------------------------------------------------------------------------
/src/WebAuthentication.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Asynchronous WebServer library for Espressif MCUs
3 |
4 | Copyright (c) 2016 Hristo Gochkov. All rights reserved.
5 | This file is part of the esp8266 core for Arduino environment.
6 |
7 | This library is free software; you can redistribute it and/or
8 | modify it under the terms of the GNU Lesser General Public
9 | License as published by the Free Software Foundation; either
10 | version 2.1 of the License, or (at your option) any later version.
11 |
12 | This library is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | Lesser General Public License for more details.
16 |
17 | You should have received a copy of the GNU Lesser General Public
18 | License along with this library; if not, write to the Free Software
19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 | */
21 | #include "WebAuthentication.h"
22 | #include
23 | #ifdef ESP32
24 | #include "mbedtls/md5.h"
25 | #else
26 | #include "md5.h"
27 | #endif
28 |
29 |
30 | // Basic Auth hash = base64("username:password")
31 |
32 | bool checkBasicAuthentication(const char * hash, const char * username, const char * password){
33 | if(username == NULL || password == NULL || hash == NULL)
34 | return false;
35 |
36 | size_t toencodeLen = strlen(username)+strlen(password)+1;
37 | size_t encodedLen = base64_encode_expected_len(toencodeLen);
38 | if(strlen(hash) != encodedLen)
39 | return false;
40 |
41 | char *toencode = new char[toencodeLen+1];
42 | if(toencode == NULL){
43 | return false;
44 | }
45 | char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
46 | if(encoded == NULL){
47 | delete[] toencode;
48 | return false;
49 | }
50 | sprintf(toencode, "%s:%s", username, password);
51 | if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0){
52 | delete[] toencode;
53 | delete[] encoded;
54 | return true;
55 | }
56 | delete[] toencode;
57 | delete[] encoded;
58 | return false;
59 | }
60 |
61 | static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or more
62 | #ifdef ESP32
63 | mbedtls_md5_context _ctx;
64 | #else
65 | md5_context_t _ctx;
66 | #endif
67 | uint8_t i;
68 | uint8_t * _buf = (uint8_t*)malloc(16);
69 | if(_buf == NULL)
70 | return false;
71 | memset(_buf, 0x00, 16);
72 | #ifdef ESP32
73 | mbedtls_md5_init(&_ctx);
74 | mbedtls_md5_starts_ret(&_ctx);
75 | mbedtls_md5_update_ret(&_ctx, data, len);
76 | mbedtls_md5_finish_ret(&_ctx, _buf);
77 | #else
78 | MD5Init(&_ctx);
79 | MD5Update(&_ctx, data, len);
80 | MD5Final(_buf, &_ctx);
81 | #endif
82 | for(i = 0; i < 16; i++) {
83 | sprintf(output + (i * 2), "%02x", _buf[i]);
84 | }
85 | free(_buf);
86 | return true;
87 | }
88 |
89 | static String genRandomMD5(){
90 | #ifdef ESP8266
91 | uint32_t r = RANDOM_REG32;
92 | #else
93 | uint32_t r = rand();
94 | #endif
95 | char * out = (char*)malloc(33);
96 | if(out == NULL || !getMD5((uint8_t*)(&r), 4, out))
97 | return "";
98 | String res = String(out);
99 | free(out);
100 | return res;
101 | }
102 |
103 | static String stringMD5(const String& in){
104 | char * out = (char*)malloc(33);
105 | if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
106 | return "";
107 | String res = String(out);
108 | free(out);
109 | return res;
110 | }
111 |
112 | String generateDigestHash(const char * username, const char * password, const char * realm){
113 | if(username == NULL || password == NULL || realm == NULL){
114 | return "";
115 | }
116 | char * out = (char*)malloc(33);
117 | String res = String(username);
118 | res.concat(":");
119 | res.concat(realm);
120 | res.concat(":");
121 | String in = res;
122 | in.concat(password);
123 | if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
124 | return "";
125 | res.concat(out);
126 | free(out);
127 | return res;
128 | }
129 |
130 | String requestDigestAuthentication(const char * realm){
131 | String header = "realm=\"";
132 | if(realm == NULL)
133 | header.concat("asyncesp");
134 | else
135 | header.concat(realm);
136 | header.concat( "\", qop=\"auth\", nonce=\"");
137 | header.concat(genRandomMD5());
138 | header.concat("\", opaque=\"");
139 | header.concat(genRandomMD5());
140 | header.concat("\"");
141 | return header;
142 | }
143 |
144 | 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){
145 | if(username == NULL || password == NULL || header == NULL || method == NULL){
146 | //os_printf("AUTH FAIL: missing requred fields\n");
147 | return false;
148 | }
149 |
150 | String myHeader = String(header);
151 | int nextBreak = myHeader.indexOf(",");
152 | if(nextBreak < 0){
153 | //os_printf("AUTH FAIL: no variables\n");
154 | return false;
155 | }
156 |
157 | String myUsername = String();
158 | String myRealm = String();
159 | String myNonce = String();
160 | String myUri = String();
161 | String myResponse = String();
162 | String myQop = String();
163 | String myNc = String();
164 | String myCnonce = String();
165 |
166 | myHeader += ", ";
167 | do {
168 | String avLine = myHeader.substring(0, nextBreak);
169 | avLine.trim();
170 | myHeader = myHeader.substring(nextBreak+1);
171 | nextBreak = myHeader.indexOf(",");
172 |
173 | int eqSign = avLine.indexOf("=");
174 | if(eqSign < 0){
175 | //os_printf("AUTH FAIL: no = sign\n");
176 | return false;
177 | }
178 | String varName = avLine.substring(0, eqSign);
179 | avLine = avLine.substring(eqSign + 1);
180 | if(avLine.startsWith("\"")){
181 | avLine = avLine.substring(1, avLine.length() - 1);
182 | }
183 |
184 | if(varName.equals("username")){
185 | if(!avLine.equals(username)){
186 | //os_printf("AUTH FAIL: username\n");
187 | return false;
188 | }
189 | myUsername = avLine;
190 | } else if(varName.equals("realm")){
191 | if(realm != NULL && !avLine.equals(realm)){
192 | //os_printf("AUTH FAIL: realm\n");
193 | return false;
194 | }
195 | myRealm = avLine;
196 | } else if(varName.equals("nonce")){
197 | if(nonce != NULL && !avLine.equals(nonce)){
198 | //os_printf("AUTH FAIL: nonce\n");
199 | return false;
200 | }
201 | myNonce = avLine;
202 | } else if(varName.equals("opaque")){
203 | if(opaque != NULL && !avLine.equals(opaque)){
204 | //os_printf("AUTH FAIL: opaque\n");
205 | return false;
206 | }
207 | } else if(varName.equals("uri")){
208 | if(uri != NULL && !avLine.equals(uri)){
209 | //os_printf("AUTH FAIL: uri\n");
210 | return false;
211 | }
212 | myUri = avLine;
213 | } else if(varName.equals("response")){
214 | myResponse = avLine;
215 | } else if(varName.equals("qop")){
216 | myQop = avLine;
217 | } else if(varName.equals("nc")){
218 | myNc = avLine;
219 | } else if(varName.equals("cnonce")){
220 | myCnonce = avLine;
221 | }
222 | } while(nextBreak > 0);
223 |
224 | String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ":" + myRealm + ":" + String(password));
225 | String ha2 = String(method) + ":" + myUri;
226 | String response = ha1 + ":" + myNonce + ":" + myNc + ":" + myCnonce + ":" + myQop + ":" + stringMD5(ha2);
227 |
228 | if(myResponse.equals(stringMD5(response))){
229 | //os_printf("AUTH SUCCESS\n");
230 | return true;
231 | }
232 |
233 | //os_printf("AUTH FAIL: password\n");
234 | return false;
235 | }
236 |
--------------------------------------------------------------------------------
/src/WebAuthentication.h:
--------------------------------------------------------------------------------
1 | /*
2 | Asynchronous WebServer library for Espressif MCUs
3 |
4 | Copyright (c) 2016 Hristo Gochkov. All rights reserved.
5 | This file is part of the esp8266 core for Arduino environment.
6 |
7 | This library is free software; you can redistribute it and/or
8 | modify it under the terms of the GNU Lesser General Public
9 | License as published by the Free Software Foundation; either
10 | version 2.1 of the License, or (at your option) any later version.
11 |
12 | This library is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | Lesser General Public License for more details.
16 |
17 | You should have received a copy of the GNU Lesser General Public
18 | License along with this library; if not, write to the Free Software
19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 | */
21 |
22 | #ifndef WEB_AUTHENTICATION_H_
23 | #define WEB_AUTHENTICATION_H_
24 |
25 | #include "Arduino.h"
26 |
27 | #ifdef ESP32
28 | // Code for version 3.x
29 | #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
30 | #include "mbedtls/compat-2.x.h"
31 | #endif
32 | #endif
33 |
34 | bool checkBasicAuthentication(const char * header, const char * username, const char * password);
35 | String requestDigestAuthentication(const char * realm);
36 | 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);
37 |
38 | //for storing hashed versions on the device that can be authenticated against
39 | String generateDigestHash(const char * username, const char * password, const char * realm);
40 |
41 | #endif
42 |
--------------------------------------------------------------------------------
/src/WebHandlerImpl.h:
--------------------------------------------------------------------------------
1 | /*
2 | Asynchronous WebServer library for Espressif MCUs
3 |
4 | Copyright (c) 2016 Hristo Gochkov. All rights reserved.
5 | This file is part of the esp8266 core for Arduino environment.
6 |
7 | This library is free software; you can redistribute it and/or
8 | modify it under the terms of the GNU Lesser General Public
9 | License as published by the Free Software Foundation; either
10 | version 2.1 of the License, or (at your option) any later version.
11 |
12 | This library is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | Lesser General Public License for more details.
16 |
17 | You should have received a copy of the GNU Lesser General Public
18 | License along with this library; if not, write to the Free Software
19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 | */
21 | #ifndef ASYNCWEBSERVERHANDLERIMPL_H_
22 | #define ASYNCWEBSERVERHANDLERIMPL_H_
23 |
24 | #include
25 | #ifdef ASYNCWEBSERVER_REGEX
26 | #include
27 | #endif
28 |
29 | #include "stddef.h"
30 | #include
31 |
32 | class AsyncStaticWebHandler: public AsyncWebHandler {
33 | using File = fs::File;
34 | using FS = fs::FS;
35 | private:
36 | bool _getFile(AsyncWebServerRequest *request);
37 | bool _fileExists(AsyncWebServerRequest *request, const String& path);
38 | uint8_t _countBits(const uint8_t value) const;
39 | protected:
40 | FS _fs;
41 | String _uri;
42 | String _path;
43 | String _default_file;
44 | String _cache_control;
45 | String _last_modified;
46 | AwsTemplateProcessor _callback;
47 | bool _isDir;
48 | bool _gzipFirst;
49 | uint8_t _gzipStats;
50 | public:
51 | AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control);
52 | virtual bool canHandle(AsyncWebServerRequest *request) override final;
53 | virtual void handleRequest(AsyncWebServerRequest *request) override final;
54 | AsyncStaticWebHandler& setIsDir(bool isDir);
55 | AsyncStaticWebHandler& setDefaultFile(const char* filename);
56 | AsyncStaticWebHandler& setCacheControl(const char* cache_control);
57 | AsyncStaticWebHandler& setLastModified(const char* last_modified);
58 | AsyncStaticWebHandler& setLastModified(struct tm* last_modified);
59 | #ifdef ESP8266
60 | AsyncStaticWebHandler& setLastModified(time_t last_modified);
61 | AsyncStaticWebHandler& setLastModified(); //sets to current time. Make sure sntp is runing and time is updated
62 | #endif
63 | AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {_callback = newCallback; return *this;}
64 | };
65 |
66 | class AsyncCallbackWebHandler: public AsyncWebHandler {
67 | private:
68 | protected:
69 | String _uri;
70 | WebRequestMethodComposite _method;
71 | ArRequestHandlerFunction _onRequest;
72 | ArUploadHandlerFunction _onUpload;
73 | ArBodyHandlerFunction _onBody;
74 | bool _isRegex;
75 | public:
76 | AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {}
77 | void setUri(const String& uri){
78 | _uri = uri;
79 | _isRegex = uri.startsWith("^") && uri.endsWith("$");
80 | }
81 | void setMethod(WebRequestMethodComposite method){ _method = method; }
82 | void onRequest(ArRequestHandlerFunction fn){ _onRequest = fn; }
83 | void onUpload(ArUploadHandlerFunction fn){ _onUpload = fn; }
84 | void onBody(ArBodyHandlerFunction fn){ _onBody = fn; }
85 |
86 | virtual bool canHandle(AsyncWebServerRequest *request) override final{
87 |
88 | if(!_onRequest)
89 | return false;
90 |
91 | if(!(_method & request->method()))
92 | return false;
93 |
94 | #ifdef ASYNCWEBSERVER_REGEX
95 | if (_isRegex) {
96 | std::regex pattern(_uri.c_str());
97 | std::smatch matches;
98 | std::string s(request->url().c_str());
99 | if(std::regex_search(s, matches, pattern)) {
100 | for (size_t i = 1; i < matches.size(); ++i) { // start from 1
101 | request->_addPathParam(matches[i].str().c_str());
102 | }
103 | } else {
104 | return false;
105 | }
106 | } else
107 | #endif
108 | if (_uri.length() && _uri.startsWith("/*.")) {
109 | String uriTemplate = String (_uri);
110 | uriTemplate = uriTemplate.substring(uriTemplate.lastIndexOf("."));
111 | if (!request->url().endsWith(uriTemplate))
112 | return false;
113 | }
114 | else
115 | if (_uri.length() && _uri.endsWith("*")) {
116 | String uriTemplate = String(_uri);
117 | uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
118 | if (!request->url().startsWith(uriTemplate))
119 | return false;
120 | }
121 | else if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
122 | return false;
123 |
124 | request->addInterestingHeader("ANY");
125 | return true;
126 | }
127 |
128 | virtual void handleRequest(AsyncWebServerRequest *request) override final {
129 | if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
130 | return request->requestAuthentication();
131 | if(_onRequest)
132 | _onRequest(request);
133 | else
134 | request->send(500);
135 | }
136 | virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final {
137 | if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
138 | return request->requestAuthentication();
139 | if(_onUpload)
140 | _onUpload(request, filename, index, data, len, final);
141 | }
142 | virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
143 | if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
144 | return request->requestAuthentication();
145 | if(_onBody)
146 | _onBody(request, data, len, index, total);
147 | }
148 | virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
149 | };
150 |
151 | #endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */
152 |
--------------------------------------------------------------------------------
/src/WebHandlers.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Asynchronous WebServer library for Espressif MCUs
3 |
4 | Copyright (c) 2016 Hristo Gochkov. All rights reserved.
5 | This file is part of the esp8266 core for Arduino environment.
6 |
7 | This library is free software; you can redistribute it and/or
8 | modify it under the terms of the GNU Lesser General Public
9 | License as published by the Free Software Foundation; either
10 | version 2.1 of the License, or (at your option) any later version.
11 |
12 | This library is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | Lesser General Public License for more details.
16 |
17 | You should have received a copy of the GNU Lesser General Public
18 | License along with this library; if not, write to the Free Software
19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 | */
21 | #include "ESPAsyncWebSrv.h"
22 | #include "WebHandlerImpl.h"
23 |
24 | AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control)
25 | : _fs(fs), _uri(uri), _path(path), _default_file("index.htm"), _cache_control(cache_control), _last_modified(""), _callback(nullptr)
26 | {
27 | // Ensure leading '/'
28 | if (_uri.length() == 0 || _uri[0] != '/') _uri = "/" + _uri;
29 | if (_path.length() == 0 || _path[0] != '/') _path = "/" + _path;
30 |
31 | // If path ends with '/' we assume a hint that this is a directory to improve performance.
32 | // However - if it does not end with '/' we, can't assume a file, path can still be a directory.
33 | _isDir = _path[_path.length()-1] == '/';
34 |
35 | // Remove the trailing '/' so we can handle default file
36 | // Notice that root will be "" not "/"
37 | if (_uri[_uri.length()-1] == '/') _uri = _uri.substring(0, _uri.length()-1);
38 | if (_path[_path.length()-1] == '/') _path = _path.substring(0, _path.length()-1);
39 |
40 | // Reset stats
41 | _gzipFirst = false;
42 | _gzipStats = 0xF8;
43 | }
44 |
45 | AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir){
46 | _isDir = isDir;
47 | return *this;
48 | }
49 |
50 | AsyncStaticWebHandler& AsyncStaticWebHandler::setDefaultFile(const char* filename){
51 | _default_file = String(filename);
52 | return *this;
53 | }
54 |
55 | AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_control){
56 | _cache_control = String(cache_control);
57 | return *this;
58 | }
59 |
60 | AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_modified){
61 | _last_modified = String(last_modified);
62 | return *this;
63 | }
64 |
65 | AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified){
66 | char result[30];
67 | strftime (result,30,"%a, %d %b %Y %H:%M:%S %Z", last_modified);
68 | return setLastModified((const char *)result);
69 | }
70 |
71 | #ifdef ESP8266
72 | AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(time_t last_modified){
73 | return setLastModified((struct tm *)gmtime(&last_modified));
74 | }
75 |
76 | AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(){
77 | time_t last_modified;
78 | if(time(&last_modified) == 0) //time is not yet set
79 | return *this;
80 | return setLastModified(last_modified);
81 | }
82 | #endif
83 | bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){
84 | if(request->method() != HTTP_GET
85 | || !request->url().startsWith(_uri)
86 | || !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP)
87 | ){
88 | return false;
89 | }
90 | if (_getFile(request)) {
91 | // We interested in "If-Modified-Since" header to check if file was modified
92 | if (_last_modified.length())
93 | request->addInterestingHeader("If-Modified-Since");
94 |
95 | if(_cache_control.length())
96 | request->addInterestingHeader("If-None-Match");
97 |
98 | DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n");
99 | return true;
100 | }
101 |
102 | return false;
103 | }
104 |
105 | bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request)
106 | {
107 | // Remove the found uri
108 | String path = request->url().substring(_uri.length());
109 |
110 | // We can skip the file check and look for default if request is to the root of a directory or that request path ends with '/'
111 | bool canSkipFileCheck = (_isDir && path.length() == 0) || (path.length() && path[path.length()-1] == '/');
112 |
113 | path = _path + path;
114 |
115 | // Do we have a file or .gz file
116 | if (!canSkipFileCheck && _fileExists(request, path))
117 | return true;
118 |
119 | // Can't handle if not default file
120 | if (_default_file.length() == 0)
121 | return false;
122 |
123 | // Try to add default file, ensure there is a trailing '/' ot the path.
124 | if (path.length() == 0 || path[path.length()-1] != '/')
125 | path += "/";
126 | path += _default_file;
127 |
128 | return _fileExists(request, path);
129 | }
130 |
131 | #ifdef ESP32
132 | #define FILE_IS_REAL(f) (f == true && !f.isDirectory())
133 | #else
134 | #define FILE_IS_REAL(f) (f == true)
135 | #endif
136 |
137 | bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const String& path)
138 | {
139 | bool fileFound = false;
140 | bool gzipFound = false;
141 |
142 | String gzip = path + ".gz";
143 |
144 | if (_gzipFirst) {
145 | request->_tempFile = _fs.open(gzip, "r");
146 | gzipFound = FILE_IS_REAL(request->_tempFile);
147 | if (!gzipFound){
148 | request->_tempFile = _fs.open(path, "r");
149 | fileFound = FILE_IS_REAL(request->_tempFile);
150 | }
151 | } else {
152 | request->_tempFile = _fs.open(path, "r");
153 | fileFound = FILE_IS_REAL(request->_tempFile);
154 | if (!fileFound){
155 | request->_tempFile = _fs.open(gzip, "r");
156 | gzipFound = FILE_IS_REAL(request->_tempFile);
157 | }
158 | }
159 |
160 | bool found = fileFound || gzipFound;
161 |
162 | if (found) {
163 | // Extract the file name from the path and keep it in _tempObject
164 | size_t pathLen = path.length();
165 | char * _tempPath = (char*)malloc(pathLen+1);
166 | snprintf(_tempPath, pathLen+1, "%s", path.c_str());
167 | request->_tempObject = (void*)_tempPath;
168 |
169 | // Calculate gzip statistic
170 | _gzipStats = (_gzipStats << 1) + (gzipFound ? 1 : 0);
171 | if (_gzipStats == 0x00) _gzipFirst = false; // All files are not gzip
172 | else if (_gzipStats == 0xFF) _gzipFirst = true; // All files are gzip
173 | else _gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first
174 | }
175 |
176 | return found;
177 | }
178 |
179 | uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const
180 | {
181 | uint8_t w = value;
182 | uint8_t n;
183 | for (n=0; w!=0; n++) w&=w-1;
184 | return n;
185 | }
186 |
187 | void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
188 | {
189 | // Get the filename from request->_tempObject and free it
190 | String filename = String((char*)request->_tempObject);
191 | free(request->_tempObject);
192 | request->_tempObject = NULL;
193 | if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
194 | return request->requestAuthentication();
195 |
196 | if (request->_tempFile == true) {
197 | String etag = String(request->_tempFile.size());
198 | if (_last_modified.length() && _last_modified == request->header("If-Modified-Since")) {
199 | request->_tempFile.close();
200 | request->send(304); // Not modified
201 | } else if (_cache_control.length() && request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag)) {
202 | request->_tempFile.close();
203 | AsyncWebServerResponse * response = new AsyncBasicResponse(304); // Not modified
204 | response->addHeader("Cache-Control", _cache_control);
205 | response->addHeader("ETag", etag);
206 | request->send(response);
207 | } else {
208 | AsyncWebServerResponse * response = new AsyncFileResponse(request->_tempFile, filename, String(), false, _callback);
209 | if (_last_modified.length())
210 | response->addHeader("Last-Modified", _last_modified);
211 | if (_cache_control.length()){
212 | response->addHeader("Cache-Control", _cache_control);
213 | response->addHeader("ETag", etag);
214 | }
215 | request->send(response);
216 | }
217 | } else {
218 | request->send(404);
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/src/WebResponseImpl.h:
--------------------------------------------------------------------------------
1 | /*
2 | Asynchronous WebServer library for Espressif MCUs
3 |
4 | Copyright (c) 2016 Hristo Gochkov. All rights reserved.
5 | This file is part of the esp8266 core for Arduino environment.
6 |
7 | This library is free software; you can redistribute it and/or
8 | modify it under the terms of the GNU Lesser General Public
9 | License as published by the Free Software Foundation; either
10 | version 2.1 of the License, or (at your option) any later version.
11 |
12 | This library is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | Lesser General Public License for more details.
16 |
17 | You should have received a copy of the GNU Lesser General Public
18 | License along with this library; if not, write to the Free Software
19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 | */
21 | #ifndef ASYNCWEBSERVERRESPONSEIMPL_H_
22 | #define ASYNCWEBSERVERRESPONSEIMPL_H_
23 |
24 | #ifdef Arduino_h
25 | // arduino is not compatible with std::vector
26 | #undef min
27 | #undef max
28 | #endif
29 | #include
30 | // It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max.
31 |
32 | class AsyncBasicResponse: public AsyncWebServerResponse {
33 | private:
34 | String _content;
35 | public:
36 | AsyncBasicResponse(int code, const String& contentType=String(), const String& content=String());
37 | void _respond(AsyncWebServerRequest *request);
38 | size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
39 | bool _sourceValid() const { return true; }
40 | };
41 |
42 | class AsyncAbstractResponse: public AsyncWebServerResponse {
43 | private:
44 | String _head;
45 | // Data is inserted into cache at begin().
46 | // This is inefficient with vector, but if we use some other container,
47 | // we won't be able to access it as contiguous array of bytes when reading from it,
48 | // so by gaining performance in one place, we'll lose it in another.
49 | std::vector _cache;
50 | size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len);
51 | size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen);
52 | protected:
53 | AwsTemplateProcessor _callback;
54 | public:
55 | AsyncAbstractResponse(AwsTemplateProcessor callback=nullptr);
56 | void _respond(AsyncWebServerRequest *request);
57 | size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
58 | bool _sourceValid() const { return false; }
59 | virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; }
60 | };
61 |
62 | #ifndef TEMPLATE_PLACEHOLDER
63 | #define TEMPLATE_PLACEHOLDER '%'
64 | #endif
65 |
66 | #define TEMPLATE_PARAM_NAME_LENGTH 32
67 | class AsyncFileResponse: public AsyncAbstractResponse {
68 | using File = fs::File;
69 | using FS = fs::FS;
70 | private:
71 | File _content;
72 | String _path;
73 | void _setContentType(const String& path);
74 | public:
75 | AsyncFileResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
76 | AsyncFileResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
77 | ~AsyncFileResponse();
78 | bool _sourceValid() const { return !!(_content); }
79 | virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
80 | };
81 |
82 | class AsyncStreamResponse: public AsyncAbstractResponse {
83 | private:
84 | Stream *_content;
85 | public:
86 | AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
87 | bool _sourceValid() const { return !!(_content); }
88 | virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
89 | };
90 |
91 | class AsyncCallbackResponse: public AsyncAbstractResponse {
92 | private:
93 | AwsResponseFiller _content;
94 | size_t _filledLength;
95 | public:
96 | AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
97 | bool _sourceValid() const { return !!(_content); }
98 | virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
99 | };
100 |
101 | class AsyncChunkedResponse: public AsyncAbstractResponse {
102 | private:
103 | AwsResponseFiller _content;
104 | size_t _filledLength;
105 | public:
106 | AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
107 | bool _sourceValid() const { return !!(_content); }
108 | virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
109 | };
110 |
111 | class AsyncProgmemResponse: public AsyncAbstractResponse {
112 | private:
113 | const uint8_t * _content;
114 | size_t _readLength;
115 | public:
116 | AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
117 | bool _sourceValid() const { return true; }
118 | virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
119 | };
120 |
121 | class cbuf;
122 |
123 | class AsyncResponseStream: public AsyncAbstractResponse, public Print {
124 | private:
125 | cbuf *_content;
126 | public:
127 | AsyncResponseStream(const String& contentType, size_t bufferSize);
128 | ~AsyncResponseStream();
129 | bool _sourceValid() const { return (_state < RESPONSE_END); }
130 | virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
131 | size_t write(const uint8_t *data, size_t len);
132 | size_t write(uint8_t data);
133 | using Print::write;
134 | };
135 |
136 | #endif /* ASYNCWEBSERVERRESPONSEIMPL_H_ */
137 |
--------------------------------------------------------------------------------
/src/WebResponses.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Asynchronous WebServer library for Espressif MCUs
3 |
4 | Copyright (c) 2016 Hristo Gochkov. All rights reserved.
5 | This file is part of the esp8266 core for Arduino environment.
6 |
7 | This library is free software; you can redistribute it and/or
8 | modify it under the terms of the GNU Lesser General Public
9 | License as published by the Free Software Foundation; either
10 | version 2.1 of the License, or (at your option) any later version.
11 |
12 | This library is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | Lesser General Public License for more details.
16 |
17 | You should have received a copy of the GNU Lesser General Public
18 | License along with this library; if not, write to the Free Software
19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 | */
21 | #include "ESPAsyncWebSrv.h"
22 | #include "WebResponseImpl.h"
23 | #include "cbuf.h"
24 |
25 | // Since ESP8266 does not link memchr by default, here's its implementation.
26 | void* memchr(void* ptr, int ch, size_t count)
27 | {
28 | unsigned char* p = static_cast(ptr);
29 | while(count--)
30 | if(*p++ == static_cast(ch))
31 | return --p;
32 | return nullptr;
33 | }
34 |
35 |
36 | /*
37 | * Abstract Response
38 | * */
39 | const char* AsyncWebServerResponse::_responseCodeToString(int code) {
40 | switch (code) {
41 | case 100: return "Continue";
42 | case 101: return "Switching Protocols";
43 | case 200: return "OK";
44 | case 201: return "Created";
45 | case 202: return "Accepted";
46 | case 203: return "Non-Authoritative Information";
47 | case 204: return "No Content";
48 | case 205: return "Reset Content";
49 | case 206: return "Partial Content";
50 | case 300: return "Multiple Choices";
51 | case 301: return "Moved Permanently";
52 | case 302: return "Found";
53 | case 303: return "See Other";
54 | case 304: return "Not Modified";
55 | case 305: return "Use Proxy";
56 | case 307: return "Temporary Redirect";
57 | case 400: return "Bad Request";
58 | case 401: return "Unauthorized";
59 | case 402: return "Payment Required";
60 | case 403: return "Forbidden";
61 | case 404: return "Not Found";
62 | case 405: return "Method Not Allowed";
63 | case 406: return "Not Acceptable";
64 | case 407: return "Proxy Authentication Required";
65 | case 408: return "Request Time-out";
66 | case 409: return "Conflict";
67 | case 410: return "Gone";
68 | case 411: return "Length Required";
69 | case 412: return "Precondition Failed";
70 | case 413: return "Request Entity Too Large";
71 | case 414: return "Request-URI Too Large";
72 | case 415: return "Unsupported Media Type";
73 | case 416: return "Requested range not satisfiable";
74 | case 417: return "Expectation Failed";
75 | case 500: return "Internal Server Error";
76 | case 501: return "Not Implemented";
77 | case 502: return "Bad Gateway";
78 | case 503: return "Service Unavailable";
79 | case 504: return "Gateway Time-out";
80 | case 505: return "HTTP Version not supported";
81 | default: return "";
82 | }
83 | }
84 |
85 | AsyncWebServerResponse::AsyncWebServerResponse()
86 | : _code(0)
87 | , _headers(LinkedList([](AsyncWebHeader *h){ delete h; }))
88 | , _contentType()
89 | , _contentLength(0)
90 | , _sendContentLength(true)
91 | , _chunked(false)
92 | , _headLength(0)
93 | , _sentLength(0)
94 | , _ackedLength(0)
95 | , _writtenLength(0)
96 | , _state(RESPONSE_SETUP)
97 | {
98 | for(auto header: DefaultHeaders::Instance()) {
99 | _headers.add(new AsyncWebHeader(header->name(), header->value()));
100 | }
101 | }
102 |
103 | AsyncWebServerResponse::~AsyncWebServerResponse(){
104 | _headers.free();
105 | }
106 |
107 | void AsyncWebServerResponse::setCode(int code){
108 | if(_state == RESPONSE_SETUP)
109 | _code = code;
110 | }
111 |
112 | void AsyncWebServerResponse::setContentLength(size_t len){
113 | if(_state == RESPONSE_SETUP)
114 | _contentLength = len;
115 | }
116 |
117 | void AsyncWebServerResponse::setContentType(const String& type){
118 | if(_state == RESPONSE_SETUP)
119 | _contentType = type;
120 | }
121 |
122 | void AsyncWebServerResponse::addHeader(const String& name, const String& value){
123 | _headers.add(new AsyncWebHeader(name, value));
124 | }
125 |
126 | String AsyncWebServerResponse::_assembleHead(uint8_t version){
127 | if(version){
128 | addHeader("Accept-Ranges","none");
129 | if(_chunked)
130 | addHeader("Transfer-Encoding","chunked");
131 | }
132 | String out = String();
133 | int bufSize = 300;
134 | char buf[bufSize];
135 |
136 | snprintf(buf, bufSize, "HTTP/1.%d %d %s\r\n", version, _code, _responseCodeToString(_code));
137 | out.concat(buf);
138 |
139 | if(_sendContentLength) {
140 | snprintf(buf, bufSize, "Content-Length: %d\r\n", _contentLength);
141 | out.concat(buf);
142 | }
143 | if(_contentType.length()) {
144 | snprintf(buf, bufSize, "Content-Type: %s\r\n", _contentType.c_str());
145 | out.concat(buf);
146 | }
147 |
148 | for(const auto& header: _headers){
149 | snprintf(buf, bufSize, "%s: %s\r\n", header->name().c_str(), header->value().c_str());
150 | out.concat(buf);
151 | }
152 | _headers.free();
153 |
154 | out.concat("\r\n");
155 | _headLength = out.length();
156 | return out;
157 | }
158 |
159 | bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; }
160 | bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; }
161 | bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; }
162 | bool AsyncWebServerResponse::_sourceValid() const { return false; }
163 | void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request){ _state = RESPONSE_END; request->client()->close(); }
164 | size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ (void)request; (void)len; (void)time; return 0; }
165 |
166 | /*
167 | * String/Code Response
168 | * */
169 | AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const String& content){
170 | _code = code;
171 | _content = content;
172 | _contentType = contentType;
173 | if(_content.length()){
174 | _contentLength = _content.length();
175 | if(!_contentType.length())
176 | _contentType = "text/plain";
177 | }
178 | addHeader("Connection","close");
179 | }
180 |
181 | void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){
182 | _state = RESPONSE_HEADERS;
183 | String out = _assembleHead(request->version());
184 | size_t outLen = out.length();
185 | size_t space = request->client()->space();
186 | if(!_contentLength && space >= outLen){
187 | _writtenLength += request->client()->write(out.c_str(), outLen);
188 | _state = RESPONSE_WAIT_ACK;
189 | } else if(_contentLength && space >= outLen + _contentLength){
190 | out += _content;
191 | outLen += _contentLength;
192 | _writtenLength += request->client()->write(out.c_str(), outLen);
193 | _state = RESPONSE_WAIT_ACK;
194 | } else if(space && space < outLen){
195 | String partial = out.substring(0, space);
196 | _content = out.substring(space) + _content;
197 | _contentLength += outLen - space;
198 | _writtenLength += request->client()->write(partial.c_str(), partial.length());
199 | _state = RESPONSE_CONTENT;
200 | } else if(space > outLen && space < (outLen + _contentLength)){
201 | size_t shift = space - outLen;
202 | outLen += shift;
203 | _sentLength += shift;
204 | out += _content.substring(0, shift);
205 | _content = _content.substring(shift);
206 | _writtenLength += request->client()->write(out.c_str(), outLen);
207 | _state = RESPONSE_CONTENT;
208 | } else {
209 | _content = out + _content;
210 | _contentLength += outLen;
211 | _state = RESPONSE_CONTENT;
212 | }
213 | }
214 |
215 | size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
216 | (void)time;
217 | _ackedLength += len;
218 | if(_state == RESPONSE_CONTENT){
219 | size_t available = _contentLength - _sentLength;
220 | size_t space = request->client()->space();
221 | //we can fit in this packet
222 | if(space > available){
223 | _writtenLength += request->client()->write(_content.c_str(), available);
224 | _content = String();
225 | _state = RESPONSE_WAIT_ACK;
226 | return available;
227 | }
228 | //send some data, the rest on ack
229 | String out = _content.substring(0, space);
230 | _content = _content.substring(space);
231 | _sentLength += space;
232 | _writtenLength += request->client()->write(out.c_str(), space);
233 | return space;
234 | } else if(_state == RESPONSE_WAIT_ACK){
235 | if(_ackedLength >= _writtenLength){
236 | _state = RESPONSE_END;
237 | }
238 | }
239 | return 0;
240 | }
241 |
242 |
243 | /*
244 | * Abstract Response
245 | * */
246 |
247 | AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback): _callback(callback)
248 | {
249 | // In case of template processing, we're unable to determine real response size
250 | if(callback) {
251 | _contentLength = 0;
252 | _sendContentLength = false;
253 | _chunked = true;
254 | }
255 | }
256 |
257 | void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){
258 | addHeader("Connection","close");
259 | _head = _assembleHead(request->version());
260 | _state = RESPONSE_HEADERS;
261 | _ack(request, 0, 0);
262 | }
263 |
264 | size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
265 | (void)time;
266 | if(!_sourceValid()){
267 | _state = RESPONSE_FAILED;
268 | request->client()->close();
269 | return 0;
270 | }
271 | _ackedLength += len;
272 | size_t space = request->client()->space();
273 |
274 | size_t headLen = _head.length();
275 | if(_state == RESPONSE_HEADERS){
276 | if(space >= headLen){
277 | _state = RESPONSE_CONTENT;
278 | space -= headLen;
279 | } else {
280 | String out = _head.substring(0, space);
281 | _head = _head.substring(space);
282 | _writtenLength += request->client()->write(out.c_str(), out.length());
283 | return out.length();
284 | }
285 | }
286 |
287 | if(_state == RESPONSE_CONTENT){
288 | size_t outLen;
289 | if(_chunked){
290 | if(space <= 8){
291 | return 0;
292 | }
293 | outLen = space;
294 | } else if(!_sendContentLength){
295 | outLen = space;
296 | } else {
297 | outLen = ((_contentLength - _sentLength) > space)?space:(_contentLength - _sentLength);
298 | }
299 |
300 | uint8_t *buf = (uint8_t *)malloc(outLen+headLen);
301 | if (!buf) {
302 | // os_printf("_ack malloc %d failed\n", outLen+headLen);
303 | return 0;
304 | }
305 |
306 | if(headLen){
307 | memcpy(buf, _head.c_str(), _head.length());
308 | }
309 |
310 | size_t readLen = 0;
311 |
312 | if(_chunked){
313 | // HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added.
314 | // See RFC2616 sections 2, 3.6.1.
315 | readLen = _fillBufferAndProcessTemplates(buf+headLen+6, outLen - 8);
316 | if(readLen == RESPONSE_TRY_AGAIN){
317 | free(buf);
318 | return 0;
319 | }
320 | outLen = sprintf((char*)buf+headLen, "%x", readLen) + headLen;
321 | while(outLen < headLen + 4) buf[outLen++] = ' ';
322 | buf[outLen++] = '\r';
323 | buf[outLen++] = '\n';
324 | outLen += readLen;
325 | buf[outLen++] = '\r';
326 | buf[outLen++] = '\n';
327 | } else {
328 | readLen = _fillBufferAndProcessTemplates(buf+headLen, outLen);
329 | if(readLen == RESPONSE_TRY_AGAIN){
330 | free(buf);
331 | return 0;
332 | }
333 | outLen = readLen + headLen;
334 | }
335 |
336 | if(headLen){
337 | _head = String();
338 | }
339 |
340 | if(outLen){
341 | _writtenLength += request->client()->write((const char*)buf, outLen);
342 | }
343 |
344 | if(_chunked){
345 | _sentLength += readLen;
346 | } else {
347 | _sentLength += outLen - headLen;
348 | }
349 |
350 | free(buf);
351 |
352 | if((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)){
353 | _state = RESPONSE_WAIT_ACK;
354 | }
355 | return outLen;
356 |
357 | } else if(_state == RESPONSE_WAIT_ACK){
358 | if(!_sendContentLength || _ackedLength >= _writtenLength){
359 | _state = RESPONSE_END;
360 | if(!_chunked && !_sendContentLength)
361 | request->client()->close(true);
362 | }
363 | }
364 | return 0;
365 | }
366 |
367 | size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len)
368 | {
369 | // If we have something in cache, copy it to buffer
370 | const size_t readFromCache = std::min(len, _cache.size());
371 | if(readFromCache) {
372 | memcpy(data, _cache.data(), readFromCache);
373 | _cache.erase(_cache.begin(), _cache.begin() + readFromCache);
374 | }
375 | // If we need to read more...
376 | const size_t needFromFile = len - readFromCache;
377 | const size_t readFromContent = _fillBuffer(data + readFromCache, needFromFile);
378 | return readFromCache + readFromContent;
379 | }
380 |
381 | size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len)
382 | {
383 | if(!_callback)
384 | return _fillBuffer(data, len);
385 |
386 | const size_t originalLen = len;
387 | len = _readDataFromCacheOrContent(data, len);
388 | // Now we've read 'len' bytes, either from cache or from file
389 | // Search for template placeholders
390 | uint8_t* pTemplateStart = data;
391 | while((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t*)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1]
392 | uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr;
393 | // temporary buffer to hold parameter name
394 | uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1];
395 | String paramName;
396 | // If closing placeholder is found:
397 | if(pTemplateEnd) {
398 | // prepare argument to callback
399 | const size_t paramNameLength = std::min(sizeof(buf) - 1, (unsigned int)(pTemplateEnd - pTemplateStart - 1));
400 | if(paramNameLength) {
401 | memcpy(buf, pTemplateStart + 1, paramNameLength);
402 | buf[paramNameLength] = 0;
403 | paramName = String(reinterpret_cast(buf));
404 | } else { // double percent sign encountered, this is single percent sign escaped.
405 | // remove the 2nd percent sign
406 | memmove(pTemplateEnd, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
407 | len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1;
408 | ++pTemplateStart;
409 | }
410 | } else if(&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data
411 | memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart);
412 | const size_t readFromCacheOrContent = _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1));
413 | if(readFromCacheOrContent) {
414 | pTemplateEnd = (uint8_t*)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent);
415 | if(pTemplateEnd) {
416 | // prepare argument to callback
417 | *pTemplateEnd = 0;
418 | paramName = String(reinterpret_cast(buf));
419 | // Copy remaining read-ahead data into cache
420 | _cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
421 | pTemplateEnd = &data[len - 1];
422 | }
423 | else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position
424 | {
425 | // but first, store read file data in cache
426 | _cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
427 | ++pTemplateStart;
428 | }
429 | }
430 | else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
431 | ++pTemplateStart;
432 | }
433 | else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
434 | ++pTemplateStart;
435 | if(paramName.length()) {
436 | // call callback and replace with result.
437 | // Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value.
438 | // Data after pTemplateEnd may need to be moved.
439 | // The first byte of data after placeholder is located at pTemplateEnd + 1.
440 | // It should be located at pTemplateStart + numBytesCopied (to begin right after inserted parameter value).
441 | const String paramValue(_callback(paramName));
442 | const char* pvstr = paramValue.c_str();
443 | const unsigned int pvlen = paramValue.length();
444 | const size_t numBytesCopied = std::min(pvlen, static_cast(&data[originalLen - 1] - pTemplateStart + 1));
445 | // make room for param value
446 | // 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store
447 | if((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) {
448 | _cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]);
449 | //2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end
450 | memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied);
451 | len = originalLen; // fix issue with truncated data, not sure if it has any side effects
452 | } else if(pTemplateEnd + 1 != pTemplateStart + numBytesCopied)
453 | //2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit.
454 | // Move the entire data after the placeholder
455 | memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
456 | // 3. replace placeholder with actual value
457 | memcpy(pTemplateStart, pvstr, numBytesCopied);
458 | // If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer)
459 | if(numBytesCopied < pvlen) {
460 | _cache.insert(_cache.begin(), pvstr + numBytesCopied, pvstr + pvlen);
461 | } else if(pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text...
462 | // there is some free room, fill it from cache
463 | const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied;
464 | const size_t totalFreeRoom = originalLen - len + roomFreed;
465 | len += _readDataFromCacheOrContent(&data[len - roomFreed], totalFreeRoom) - roomFreed;
466 | } else { // result is copied fully; it is longer than placeholder text
467 | const size_t roomTaken = pTemplateStart + numBytesCopied - pTemplateEnd - 1;
468 | len = std::min(len + roomTaken, originalLen);
469 | }
470 | }
471 | } // while(pTemplateStart)
472 | return len;
473 | }
474 |
475 |
476 | /*
477 | * File Response
478 | * */
479 |
480 | AsyncFileResponse::~AsyncFileResponse(){
481 | if(_content)
482 | _content.close();
483 | }
484 |
485 | void AsyncFileResponse::_setContentType(const String& path){
486 | if (path.endsWith(".html")) _contentType = "text/html";
487 | else if (path.endsWith(".htm")) _contentType = "text/html";
488 | else if (path.endsWith(".css")) _contentType = "text/css";
489 | else if (path.endsWith(".json")) _contentType = "application/json";
490 | else if (path.endsWith(".js")) _contentType = "application/javascript";
491 | else if (path.endsWith(".png")) _contentType = "image/png";
492 | else if (path.endsWith(".gif")) _contentType = "image/gif";
493 | else if (path.endsWith(".jpg")) _contentType = "image/jpeg";
494 | else if (path.endsWith(".ico")) _contentType = "image/x-icon";
495 | else if (path.endsWith(".svg")) _contentType = "image/svg+xml";
496 | else if (path.endsWith(".eot")) _contentType = "font/eot";
497 | else if (path.endsWith(".woff")) _contentType = "font/woff";
498 | else if (path.endsWith(".woff2")) _contentType = "font/woff2";
499 | else if (path.endsWith(".ttf")) _contentType = "font/ttf";
500 | else if (path.endsWith(".xml")) _contentType = "text/xml";
501 | else if (path.endsWith(".pdf")) _contentType = "application/pdf";
502 | else if (path.endsWith(".zip")) _contentType = "application/zip";
503 | else if(path.endsWith(".gz")) _contentType = "application/x-gzip";
504 | else _contentType = "text/plain";
505 | }
506 |
507 | AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){
508 | _code = 200;
509 | _path = path;
510 |
511 | if(!download && !fs.exists(_path) && fs.exists(_path+".gz")){
512 | _path = _path+".gz";
513 | addHeader("Content-Encoding", "gzip");
514 | _callback = nullptr; // Unable to process zipped templates
515 | _sendContentLength = true;
516 | _chunked = false;
517 | }
518 |
519 | _content = fs.open(_path, "r");
520 | _contentLength = _content.size();
521 |
522 | if(contentType == "")
523 | _setContentType(path);
524 | else
525 | _contentType = contentType;
526 |
527 | int filenameStart = path.lastIndexOf('/') + 1;
528 | char buf[26+path.length()-filenameStart];
529 | char* filename = (char*)path.c_str() + filenameStart;
530 |
531 | if(download) {
532 | // set filename and force download
533 | snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename);
534 | } else {
535 | // set filename and force rendering
536 | snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename);
537 | }
538 | addHeader("Content-Disposition", buf);
539 | }
540 |
541 | AsyncFileResponse::AsyncFileResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){
542 | _code = 200;
543 | _path = path;
544 |
545 | if(!download && String(content.name()).endsWith(".gz") && !path.endsWith(".gz")){
546 | addHeader("Content-Encoding", "gzip");
547 | _callback = nullptr; // Unable to process gzipped templates
548 | _sendContentLength = true;
549 | _chunked = false;
550 | }
551 |
552 | _content = content;
553 | _contentLength = _content.size();
554 |
555 | if(contentType == "")
556 | _setContentType(path);
557 | else
558 | _contentType = contentType;
559 |
560 | int filenameStart = path.lastIndexOf('/') + 1;
561 | char buf[26+path.length()-filenameStart];
562 | char* filename = (char*)path.c_str() + filenameStart;
563 |
564 | if(download) {
565 | snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename);
566 | } else {
567 | snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename);
568 | }
569 | addHeader("Content-Disposition", buf);
570 | }
571 |
572 | size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){
573 | return _content.read(data, len);
574 | }
575 |
576 | /*
577 | * Stream Response
578 | * */
579 |
580 | AsyncStreamResponse::AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) {
581 | _code = 200;
582 | _content = &stream;
583 | _contentLength = len;
584 | _contentType = contentType;
585 | }
586 |
587 | size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len){
588 | size_t available = _content->available();
589 | size_t outLen = (available > len)?len:available;
590 | size_t i;
591 | for(i=0;iread();
593 | return outLen;
594 | }
595 |
596 | /*
597 | * Callback Response
598 | * */
599 |
600 | AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback): AsyncAbstractResponse(templateCallback) {
601 | _code = 200;
602 | _content = callback;
603 | _contentLength = len;
604 | if(!len)
605 | _sendContentLength = false;
606 | _contentType = contentType;
607 | _filledLength = 0;
608 | }
609 |
610 | size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){
611 | size_t ret = _content(data, len, _filledLength);
612 | if(ret != RESPONSE_TRY_AGAIN){
613 | _filledLength += ret;
614 | }
615 | return ret;
616 | }
617 |
618 | /*
619 | * Chunked Response
620 | * */
621 |
622 | AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback): AsyncAbstractResponse(processorCallback) {
623 | _code = 200;
624 | _content = callback;
625 | _contentLength = 0;
626 | _contentType = contentType;
627 | _sendContentLength = false;
628 | _chunked = true;
629 | _filledLength = 0;
630 | }
631 |
632 | size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){
633 | size_t ret = _content(data, len, _filledLength);
634 | if(ret != RESPONSE_TRY_AGAIN){
635 | _filledLength += ret;
636 | }
637 | return ret;
638 | }
639 |
640 | /*
641 | * Progmem Response
642 | * */
643 |
644 | AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) {
645 | _code = code;
646 | _content = content;
647 | _contentType = contentType;
648 | _contentLength = len;
649 | _readLength = 0;
650 | }
651 |
652 | size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){
653 | size_t left = _contentLength - _readLength;
654 | if (left > len) {
655 | memcpy_P(data, _content + _readLength, len);
656 | _readLength += len;
657 | return len;
658 | }
659 | memcpy_P(data, _content + _readLength, left);
660 | _readLength += left;
661 | return left;
662 | }
663 |
664 |
665 | /*
666 | * Response Stream (You can print/write/printf to it, up to the contentLen bytes)
667 | * */
668 |
669 | AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize){
670 | _code = 200;
671 | _contentLength = 0;
672 | _contentType = contentType;
673 | _content = new cbuf(bufferSize);
674 | }
675 |
676 | AsyncResponseStream::~AsyncResponseStream(){
677 | delete _content;
678 | }
679 |
680 | size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen){
681 | return _content->read((char*)buf, maxLen);
682 | }
683 |
684 | size_t AsyncResponseStream::write(const uint8_t *data, size_t len){
685 | if(_started())
686 | return 0;
687 |
688 | if(len > _content->room()){
689 | size_t needed = len - _content->room();
690 | _content->resizeAdd(needed);
691 | }
692 | size_t written = _content->write((const char*)data, len);
693 | _contentLength += written;
694 | return written;
695 | }
696 |
697 | size_t AsyncResponseStream::write(uint8_t data){
698 | return write(&data, 1);
699 | }
700 |
--------------------------------------------------------------------------------
/src/WebServer.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Asynchronous WebServer library for Espressif MCUs
3 |
4 | Copyright (c) 2016 Hristo Gochkov. All rights reserved.
5 | This file is part of the esp8266 core for Arduino environment.
6 |
7 | This library is free software; you can redistribute it and/or
8 | modify it under the terms of the GNU Lesser General Public
9 | License as published by the Free Software Foundation; either
10 | version 2.1 of the License, or (at your option) any later version.
11 |
12 | This library is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | Lesser General Public License for more details.
16 |
17 | You should have received a copy of the GNU Lesser General Public
18 | License along with this library; if not, write to the Free Software
19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 | */
21 | #include "ESPAsyncWebSrv.h"
22 | #include "WebHandlerImpl.h"
23 |
24 | bool ON_STA_FILTER(AsyncWebServerRequest *request) {
25 | return WiFi.localIP() == request->client()->localIP();
26 | }
27 |
28 | bool ON_AP_FILTER(AsyncWebServerRequest *request) {
29 | return WiFi.localIP() != request->client()->localIP();
30 | }
31 |
32 |
33 | AsyncWebServer::AsyncWebServer(uint16_t port)
34 | : _server(port)
35 | , _rewrites(LinkedList([](AsyncWebRewrite* r){ delete r; }))
36 | , _handlers(LinkedList([](AsyncWebHandler* h){ delete h; }))
37 | {
38 | _catchAllHandler = new AsyncCallbackWebHandler();
39 | if(_catchAllHandler == NULL)
40 | return;
41 | _server.onClient([](void *s, AsyncClient* c){
42 | if(c == NULL)
43 | return;
44 | c->setRxTimeout(3);
45 | AsyncWebServerRequest *r = new AsyncWebServerRequest((AsyncWebServer*)s, c);
46 | if(r == NULL){
47 | c->close(true);
48 | c->free();
49 | delete c;
50 | }
51 | }, this);
52 | }
53 |
54 | AsyncWebServer::~AsyncWebServer(){
55 | reset();
56 | end();
57 | if(_catchAllHandler) delete _catchAllHandler;
58 | }
59 |
60 | AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite){
61 | _rewrites.add(rewrite);
62 | return *rewrite;
63 | }
64 |
65 | bool AsyncWebServer::removeRewrite(AsyncWebRewrite *rewrite){
66 | return _rewrites.remove(rewrite);
67 | }
68 |
69 | AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to){
70 | return addRewrite(new AsyncWebRewrite(from, to));
71 | }
72 |
73 | AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler){
74 | _handlers.add(handler);
75 | return *handler;
76 | }
77 |
78 | bool AsyncWebServer::removeHandler(AsyncWebHandler *handler){
79 | return _handlers.remove(handler);
80 | }
81 |
82 | void AsyncWebServer::begin(){
83 | _server.setNoDelay(true);
84 | _server.begin();
85 | }
86 |
87 | void AsyncWebServer::end(){
88 | _server.end();
89 | }
90 |
91 | #if ASYNC_TCP_SSL_ENABLED
92 | void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void* arg){
93 | _server.onSslFileRequest(cb, arg);
94 | }
95 |
96 | void AsyncWebServer::beginSecure(const char *cert, const char *key, const char *password){
97 | _server.beginSecure(cert, key, password);
98 | }
99 | #endif
100 |
101 | void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest *request){
102 | delete request;
103 | }
104 |
105 | void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request){
106 | for(const auto& r: _rewrites){
107 | if (r->match(request)){
108 | request->_url = r->toUrl();
109 | request->_addGetParams(r->params());
110 | }
111 | }
112 | }
113 |
114 | void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request){
115 | for(const auto& h: _handlers){
116 | if (h->filter(request) && h->canHandle(request)){
117 | request->setHandler(h);
118 | return;
119 | }
120 | }
121 |
122 | request->addInterestingHeader("ANY");
123 | request->setHandler(_catchAllHandler);
124 | }
125 |
126 |
127 | AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody){
128 | AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
129 | handler->setUri(uri);
130 | handler->setMethod(method);
131 | handler->onRequest(onRequest);
132 | handler->onUpload(onUpload);
133 | handler->onBody(onBody);
134 | addHandler(handler);
135 | return *handler;
136 | }
137 |
138 | AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload){
139 | AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
140 | handler->setUri(uri);
141 | handler->setMethod(method);
142 | handler->onRequest(onRequest);
143 | handler->onUpload(onUpload);
144 | addHandler(handler);
145 | return *handler;
146 | }
147 |
148 | AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){
149 | AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
150 | handler->setUri(uri);
151 | handler->setMethod(method);
152 | handler->onRequest(onRequest);
153 | addHandler(handler);
154 | return *handler;
155 | }
156 |
157 | AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest){
158 | AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
159 | handler->setUri(uri);
160 | handler->onRequest(onRequest);
161 | addHandler(handler);
162 | return *handler;
163 | }
164 |
165 | AsyncStaticWebHandler& AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control){
166 | AsyncStaticWebHandler* handler = new AsyncStaticWebHandler(uri, fs, path, cache_control);
167 | addHandler(handler);
168 | return *handler;
169 | }
170 |
171 | void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn){
172 | _catchAllHandler->onRequest(fn);
173 | }
174 |
175 | void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn){
176 | _catchAllHandler->onUpload(fn);
177 | }
178 |
179 | void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn){
180 | _catchAllHandler->onBody(fn);
181 | }
182 |
183 | void AsyncWebServer::reset(){
184 | _rewrites.free();
185 | _handlers.free();
186 |
187 | if (_catchAllHandler != NULL){
188 | _catchAllHandler->onRequest(NULL);
189 | _catchAllHandler->onUpload(NULL);
190 | _catchAllHandler->onBody(NULL);
191 | }
192 | }
193 |
194 |
--------------------------------------------------------------------------------
/src/edit.htm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ESP Editor
6 |
135 |
609 |
610 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
--------------------------------------------------------------------------------