Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ce07fd5

Browse files
committedMay 20, 2020
Merge branch 'master' of https://github.com/esp8266/Arduino into gcc9.1
2 parents d2b4e4c + 5762712 commit ce07fd5

File tree

14 files changed

+491
-78
lines changed

14 files changed

+491
-78
lines changed
 

‎cores/esp8266/Crypto.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@
2323
THE SOFTWARE.
2424
*/
2525

26-
#include <bearssl/bearssl.h>
2726
#include "Crypto.h"
2827
#include <TypeConversion.h>
29-
28+
#include <bearssl/bearssl.h>
3029
#include <assert.h>
3130

3231
namespace TypeCast = experimental::TypeConversion;

‎cores/esp8266/Crypto.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#define __ESP8266_ARDUINO_CRYPTO_H__
2828

2929
#include <Arduino.h>
30+
#include <bearssl/bearssl_kdf.h>
3031

3132
namespace experimental
3233
{
@@ -745,8 +746,7 @@ struct HKDF
745746

746747
private:
747748

748-
// Use an opaque type to avoid #include <bearssl/bearssl.h> which drags the lib declarations into userland. The global scope prefix is required for compilation to succeed, it seems.
749-
::br_hkdf_context hkdfContext;
749+
br_hkdf_context hkdfContext;
750750
};
751751

752752

‎cores/esp8266/spiffs/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ What spiffs does not:
4949
- Presently, it does not detect or handle bad blocks.
5050
- One configuration, one binary. There's no generic spiffs binary that handles all types of configurations.
5151

52+
## NOTICE
53+
54+
0.4.0 is under construction. This is a full rewrite and will change the underlying structure. Hence, it will not be compatible with earlier versions of the filesystem. The API is the same, with minor modifications. Some config flags will be removed (as they are mandatory in 0.4.0) and some features might fall away until 0.4.1. If you have any worries or questions, it can be discussed in issue [#179](https://github.com/pellepl/spiffs/issues/179)
5255

5356
## MORE INFO
5457

‎cores/esp8266/spiffs/spiffs_check.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,17 @@ static s32_t spiffs_delete_obj_lazy(spiffs *fs, spiffs_obj_id obj_id) {
163163
return SPIFFS_OK;
164164
}
165165
SPIFFS_CHECK_RES(res);
166-
u8_t flags = 0xff & ~SPIFFS_PH_FLAG_IXDELE;
166+
u8_t flags = 0xff;
167+
#if SPIFFS_NO_BLIND_WRITES
168+
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
169+
0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags),
170+
sizeof(flags), &flags);
171+
SPIFFS_CHECK_RES(res);
172+
#endif
173+
flags &= ~SPIFFS_PH_FLAG_IXDELE;
167174
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
168175
0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags),
169-
sizeof(u8_t),
170-
(u8_t *)&flags);
176+
sizeof(flags), &flags);
171177
return res;
172178
}
173179

@@ -425,10 +431,17 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
425431
// just finalize
426432
SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n");
427433
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
428-
u8_t flags = 0xff & ~SPIFFS_PH_FLAG_FINAL;
434+
u8_t flags = 0xff;
435+
#if SPIFFS_NO_BLIND_WRITES
436+
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ,
437+
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags),
438+
sizeof(flags), &flags);
439+
SPIFFS_CHECK_RES(res);
440+
#endif
441+
flags &= ~SPIFFS_PH_FLAG_FINAL;
429442
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
430443
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags),
431-
sizeof(u8_t), (u8_t*)&flags);
444+
sizeof(flags), &flags);
432445
}
433446
}
434447
}

‎cores/esp8266/spiffs/spiffs_config.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,17 @@ typedef uint8_t u8_t;
326326
#define SPIFFS_IX_MAP 1
327327
#endif
328328

329+
// By default SPIFFS in some cases relies on the property of NOR flash that bits
330+
// cannot be set from 0 to 1 by writing and that controllers will ignore such
331+
// bit changes. This results in fewer reads as SPIFFS can in some cases perform
332+
// blind writes, with all bits set to 1 and only those it needs reset set to 0.
333+
// Most of the chips and controllers allow this behavior, so the default is to
334+
// use this technique. If your controller is one of the rare ones that don't,
335+
// turn this option on and SPIFFS will perform a read-modify-write instead.
336+
#ifndef SPIFFS_NO_BLIND_WRITES
337+
#define SPIFFS_NO_BLIND_WRITES 0
338+
#endif
339+
329340
// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function
330341
// in the api. This function will visualize all filesystem using given printf
331342
// function.
@@ -354,11 +365,20 @@ typedef uint8_t u8_t;
354365
#endif
355366
#endif
356367

368+
#ifndef SPIFFS_SECURE_ERASE
369+
#define SPIFFS_SECURE_ERASE 0
370+
#endif
371+
357372
// Types depending on configuration such as the amount of flash bytes
358373
// given to spiffs file system in total (spiffs_file_system_size),
359374
// the logical block size (log_block_size), and the logical page size
360375
// (log_page_size)
376+
//
377+
// Set SPIFFS_TYPES_OVERRIDE if you wish to have your own
378+
// definitions for these types (for example, if you want them
379+
// to be u32_t)
361380

381+
#ifndef SPIFFS_TYPES_OVERRIDE
362382
// Block index type. Make sure the size of this type can hold
363383
// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size
364384
typedef u16_t spiffs_block_ix;
@@ -373,5 +393,6 @@ typedef u16_t spiffs_obj_id;
373393
// hold the largest possible span index on the system -
374394
// i.e. (spiffs_file_system_size / log_page_size) - 1
375395
typedef u16_t spiffs_span_ix;
396+
#endif
376397

377398
#endif /* SPIFFS_CONFIG_H_ */

‎cores/esp8266/spiffs/spiffs_nucleus.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -879,8 +879,6 @@ s32_t spiffs_page_delete(
879879
spiffs *fs,
880880
spiffs_page_ix pix) {
881881
s32_t res;
882-
spiffs_page_header hdr;
883-
hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED);
884882
// mark deleted entry in source object lookup
885883
spiffs_obj_id d_obj_id = SPIFFS_OBJ_ID_DELETED;
886884
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_DELE,
@@ -893,12 +891,29 @@ s32_t spiffs_page_delete(
893891
fs->stats_p_deleted++;
894892
fs->stats_p_allocated--;
895893

894+
#if SPIFFS_SECURE_ERASE
895+
// Secure erase
896+
unsigned char data[SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)];
897+
bzero(data, sizeof(data));
898+
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE,
899+
0,
900+
SPIFFS_PAGE_TO_PADDR(fs, pix) + sizeof(spiffs_page_header), sizeof(data), data);
901+
SPIFFS_CHECK_RES(res);
902+
#endif
903+
896904
// mark deleted in source page
905+
u8_t flags = 0xff;
906+
#if SPIFFS_NO_BLIND_WRITES
907+
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ,
908+
0, SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags),
909+
sizeof(flags), &flags);
910+
SPIFFS_CHECK_RES(res);
911+
#endif
912+
flags &= ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED);
897913
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE,
898914
0,
899915
SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags),
900-
sizeof(u8_t),
901-
(u8_t *)&hdr.flags);
916+
sizeof(flags), &flags);
902917

903918
return res;
904919
}
@@ -2027,7 +2042,7 @@ s32_t spiffs_object_read(
20272042
// remaining data in page
20282043
len_to_read = MIN(len_to_read, SPIFFS_DATA_PAGE_SIZE(fs) - (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)));
20292044
// remaining data in file
2030-
len_to_read = MIN(len_to_read, fd->size);
2045+
len_to_read = MIN(len_to_read, fd->size - cur_offset);
20312046
SPIFFS_DBG("read: offset:" _SPIPRIi " rd:" _SPIPRIi " data spix:" _SPIPRIsp " is data_pix:" _SPIPRIpg " addr:" _SPIPRIad "\n", cur_offset, len_to_read, data_spix, data_pix,
20322047
(u32_t)(SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))));
20332048
if (len_to_read <= 0) {

‎cores/esp8266/spiffs/spiffs_nucleus.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ extern "C" {
147147

148148

149149

150-
#if defined(__GNUC__) || defined(__clang__)
151-
/* For GCC and clang */
150+
#if defined(__GNUC__) || defined(__clang__) || defined(__TI_COMPILER_VERSION__)
151+
/* For GCC, clang and TI compilers */
152152
#define SPIFFS_PACKED __attribute__((packed))
153153
#elif defined(__ICCARM__) || defined(__CC_ARM)
154154
/* For IAR ARM and Keil MDK-ARM compilers */
@@ -266,8 +266,8 @@ extern "C" {
266266
#define SPIFFS_FH_OFFS(fs, fh) ((fh) != 0 ? ((fh) + (fs)->cfg.fh_ix_offset) : 0)
267267
#define SPIFFS_FH_UNOFFS(fs, fh) ((fh) != 0 ? ((fh) - (fs)->cfg.fh_ix_offset) : 0)
268268
#else
269-
#define SPIFFS_FH_OFFS(fs, fh) (fh)
270-
#define SPIFFS_FH_UNOFFS(fs, fh) (fh)
269+
#define SPIFFS_FH_OFFS(fs, fh) ((spiffs_file)(fh))
270+
#define SPIFFS_FH_UNOFFS(fs, fh) ((spiffs_file)(fh))
271271
#endif
272272

273273

‎cores/esp8266/umm_malloc/umm_local.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ void ICACHE_FLASH_ATTR print_stats(int force);
4848

4949

5050
int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
51-
#define UMM_INFO_PRINTF(fmt, ...) umm_info_safe_printf_P(PSTR(fmt), ##__VA_ARGS__)
51+
#define UMM_INFO_PRINTF(fmt, ...) umm_info_safe_printf_P(PSTR4(fmt), ##__VA_ARGS__)
52+
// use PSTR4() instead of PSTR() to ensure 4-bytes alignment in Flash, whatever the default alignment of PSTR_ALIGN
5253

5354

5455
#endif
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/**
2+
reuseConnectionV2.ino
3+
4+
Created on: 22.11.2015
5+
6+
This example reuses the http connection and also restores the connection if the connection is lost
7+
*/
8+
9+
10+
#include <ESP8266WiFi.h>
11+
#include <ESP8266WiFiMulti.h>
12+
#include <ESP8266HTTPClient.h>
13+
14+
#ifndef STASSID
15+
#define STASSID "your-ssid"
16+
#define STAPSK "your-password"
17+
#endif
18+
19+
ESP8266WiFiMulti WiFiMulti;
20+
21+
HTTPClient http;
22+
WiFiClient client;
23+
24+
void setup() {
25+
26+
Serial.begin(115200);
27+
// Serial.setDebugOutput(true);
28+
29+
Serial.println();
30+
Serial.println();
31+
Serial.println("Connecting to WiFi...");
32+
33+
WiFi.mode(WIFI_STA);
34+
WiFiMulti.addAP(STASSID, STAPSK);
35+
36+
// wait for WiFi connection
37+
while ((WiFiMulti.run() != WL_CONNECTED)) {
38+
Serial.write('.');
39+
delay(500);
40+
}
41+
Serial.println(" connected to WiFi");
42+
43+
// allow reuse (if server supports it)
44+
http.setReuse(true);
45+
46+
47+
http.begin(client, "http://jigsaw.w3.org/HTTP/connection.html");
48+
//http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html");
49+
}
50+
51+
int pass = 0;
52+
53+
void loop() {
54+
// First 10 loop()s, retrieve the URL
55+
if (pass < 10) {
56+
pass++;
57+
Serial.printf("Reuse connection example, GET url for the %d time\n", pass);
58+
int httpCode = http.GET();
59+
if (httpCode > 0) {
60+
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
61+
62+
// file found at server
63+
if (httpCode == HTTP_CODE_OK) {
64+
http.writeToStream(&Serial);
65+
}
66+
} else {
67+
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
68+
// Something went wrong with the connection, try to reconnect
69+
http.end();
70+
http.begin(client, "http://jigsaw.w3.org/HTTP/connection.html");
71+
//http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html");
72+
}
73+
74+
if (pass == 10) {
75+
http.end();
76+
Serial.println("Done testing");
77+
} else {
78+
Serial.println("\n\n\nWait 5 second...\n");
79+
delay(5000);
80+
}
81+
}
82+
}

‎libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,10 @@ void HTTPClient::disconnect(bool preserveClient)
457457
#endif
458458
}
459459
} else {
460+
if (!preserveClient && _client) { // Also destroy _client if not connected()
461+
_client = nullptr;
462+
}
463+
460464
DEBUG_HTTPCLIENT("[HTTP-Client][end] tcp is closed\n");
461465
}
462466
}
@@ -970,7 +974,9 @@ int HTTPClient::writeToStream(Stream * stream)
970974
return returnError(HTTPC_ERROR_NO_STREAM);
971975
}
972976

973-
if(!connected()) {
977+
// Only return error if not connected and no data available, because otherwise ::getString() will return an error instead of an empty
978+
// string when the server returned a http code 204 (no content)
979+
if(!connected() && _transferEncoding != HTTPC_TE_IDENTITY && _size > 0) {
974980
return returnError(HTTPC_ERROR_NOT_CONNECTED);
975981
}
976982

@@ -979,11 +985,13 @@ int HTTPClient::writeToStream(Stream * stream)
979985
int ret = 0;
980986

981987
if(_transferEncoding == HTTPC_TE_IDENTITY) {
982-
ret = writeToStreamDataBlock(stream, len);
988+
if(len > 0) {
989+
ret = writeToStreamDataBlock(stream, len);
983990

984-
// have we an error?
985-
if(ret < 0) {
986-
return returnError(ret);
991+
// have we an error?
992+
if(ret < 0) {
993+
return returnError(ret);
994+
}
987995
}
988996
} else if(_transferEncoding == HTTPC_TE_CHUNKED) {
989997
int size = 0;
@@ -1198,12 +1206,8 @@ bool HTTPClient::hasHeader(const char* name)
11981206
*/
11991207
bool HTTPClient::connect(void)
12001208
{
1201-
if(connected()) {
1202-
if(_reuse) {
1203-
DEBUG_HTTPCLIENT("[HTTP-Client] connect: already connected, reusing connection\n");
1204-
} else {
1205-
DEBUG_HTTPCLIENT("[HTTP-Client] connect: already connected, try reuse!\n");
1206-
}
1209+
if(_reuse && _canReuse && connected()) {
1210+
DEBUG_HTTPCLIENT("[HTTP-Client] connect: already connected, reusing connection\n");
12071211
while(_client->available() > 0) {
12081212
_client->read();
12091213
}
@@ -1334,22 +1338,21 @@ int HTTPClient::handleHeaderResponse()
13341338
while(connected()) {
13351339
size_t len = _client->available();
13361340
if(len > 0) {
1341+
int headerSeparator = -1;
13371342
String headerLine = _client->readStringUntil('\n');
13381343

13391344
lastDataTime = millis();
13401345

13411346
DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] RX: '%s'\n", headerLine.c_str());
13421347

13431348
if (headerLine.startsWith(F("HTTP/1."))) {
1344-
if (_canReuse) {
1345-
_canReuse = (headerLine[sizeof "HTTP/1." - 1] != '0');
1346-
}
1347-
_returnCode = headerLine.substring(9, headerLine.indexOf(' ', 9)).toInt();
1348-
continue;
1349-
}
13501349

1351-
int headerSeparator = headerLine.indexOf(':');
1352-
if (headerSeparator > 0) {
1350+
constexpr auto httpVersionIdx = sizeof "HTTP/1." - 1;
1351+
_canReuse = _canReuse && (headerLine[httpVersionIdx] != '0');
1352+
_returnCode = headerLine.substring(httpVersionIdx + 2, headerLine.indexOf(' ', httpVersionIdx + 2)).toInt();
1353+
_canReuse = _canReuse && (_returnCode > 0) && (_returnCode < 500);
1354+
1355+
} else if ((headerSeparator = headerLine.indexOf(':')) > 0) {
13531356
String headerName = headerLine.substring(0, headerSeparator);
13541357
String headerValue = headerLine.substring(headerSeparator + 1);
13551358
headerValue.trim();
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
/* Multi-client Server Sent Event (aka EventSource) demo
2+
Run demo as follows:
3+
1. set SSID, password and ports, compile and run program
4+
you should see (random) updates of sensors A and B
5+
6+
2. on the client(s), register it for the event bus using a REST API call: curl -sS "http://<your ESP IP>:<your port>/rest/events/subscribe"
7+
on both server and client, you should now see that your client is registered
8+
the server sends back the location of the event bus (channel) to the client:
9+
subscription for client IP <your client's IP address>: event bus location: http://<your ESP IP>:<your port>/rest/events/<channel>
10+
11+
you will also see that the sensors are ready to broadcast state changes, but the client is not yet listening:
12+
SSEBroadcastState - client <your client IP>> registered but not listening
13+
14+
3. on the client(s), start listening for events with: curl -sS "http://<your ESP IP>:<your port>/rest/events/<channel>"
15+
if all is well, the following is being displayed on the ESP console
16+
SSEHandler - registered client with IP <your client IP address> is listening...
17+
broadcast status change to client IP <your client IP>> for sensor[A|B] with new state <some number>>
18+
every minute you will see on the ESP: SSEKeepAlive - client is still connected
19+
20+
on the client, you should see the SSE messages coming in:
21+
event: event
22+
data: { "TYPE":"KEEP-ALIVE" }
23+
event: event
24+
data: { "TYPE":"STATE", "sensorB": {"state" : 12408, "prevState": 13502} }
25+
event: event
26+
data: { "TYPE":"STATE", "sensorA": {"state" : 17664, "prevState": 49362} }
27+
28+
4. on the client, stop listening by hitting control-C
29+
on the ESP, after maximum one minute, the following message is displayed: SSEKeepAlive - client no longer connected, remove subscription
30+
if you start listening again after the time expired, the "/rest/events" handle becomes stale and "Handle not found" is returned
31+
you can also try to start listening again before the KeepAliver timer expires or simply register your client again
32+
*/
33+
34+
extern "C" {
35+
#include "c_types.h"
36+
}
37+
#include <ESP8266WiFi.h>
38+
#include <WiFiClient.h>
39+
#include <ESP8266WebServer.h>
40+
#include <ESP8266mDNS.h>
41+
#include <Ticker.h>
42+
43+
#ifndef STASSID
44+
#define STASSID "your-ssid"
45+
#define STAPSK "your-password"
46+
#endif
47+
48+
const char* ssid = STASSID;
49+
const char* password = STAPSK;
50+
const unsigned int port = 80;
51+
52+
ESP8266WebServer server(port);
53+
54+
#define SSE_MAX_CHANNELS 8 // in this simplified example, only eight SSE clients subscription allowed
55+
struct SSESubscription {
56+
IPAddress clientIP;
57+
WiFiClient client;
58+
Ticker keepAliveTimer;
59+
} subscription[SSE_MAX_CHANNELS];
60+
uint8_t subscriptionCount = 0;
61+
62+
typedef struct {
63+
const char *name;
64+
unsigned short value;
65+
Ticker update;
66+
} sensorType;
67+
sensorType sensor[2];
68+
69+
void handleNotFound() {
70+
Serial.println(F("Handle not found"));
71+
String message = "Handle Not Found\n\n";
72+
message += "URI: ";
73+
message += server.uri();
74+
message += "\nMethod: ";
75+
message += (server.method() == HTTP_GET) ? "GET" : "POST";
76+
message += "\nArguments: ";
77+
message += server.args();
78+
message += "\n";
79+
for (uint8_t i = 0; i < server.args(); i++) {
80+
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
81+
}
82+
server.send(404, "text/plain", message);
83+
}
84+
85+
void SSEKeepAlive() {
86+
for (uint8_t i = 0; i < SSE_MAX_CHANNELS; i++) {
87+
if (!(subscription[i].clientIP)) {
88+
continue;
89+
}
90+
if (subscription[i].client.connected()) {
91+
Serial.printf_P(PSTR("SSEKeepAlive - client is still listening on channel %d\n"), i);
92+
subscription[i].client.println(F("event: event\ndata: { \"TYPE\":\"KEEP-ALIVE\" }\n")); // Extra newline required by SSE standard
93+
} else {
94+
Serial.printf_P(PSTR("SSEKeepAlive - client not listening on channel %d, remove subscription\n"), i);
95+
subscription[i].keepAliveTimer.detach();
96+
subscription[i].client.flush();
97+
subscription[i].client.stop();
98+
subscription[i].clientIP = INADDR_NONE;
99+
subscriptionCount--;
100+
}
101+
}
102+
}
103+
104+
// SSEHandler handles the client connection to the event bus (client event listener)
105+
// every 60 seconds it sends a keep alive event via Ticker
106+
void SSEHandler(uint8_t channel) {
107+
WiFiClient client = server.client();
108+
SSESubscription &s = subscription[channel];
109+
if (s.clientIP != client.remoteIP()) { // IP addresses don't match, reject this client
110+
Serial.printf_P(PSTR("SSEHandler - unregistered client with IP %s tries to listen\n"), server.client().remoteIP().toString().c_str());
111+
return handleNotFound();
112+
}
113+
client.setNoDelay(true);
114+
client.setSync(true);
115+
Serial.printf_P(PSTR("SSEHandler - registered client with IP %s is listening\n"), IPAddress(s.clientIP).toString().c_str());
116+
s.client = client; // capture SSE server client connection
117+
server.setContentLength(CONTENT_LENGTH_UNKNOWN); // the payload can go on forever
118+
server.sendContent_P(PSTR("HTTP/1.1 200 OK\nContent-Type: text/event-stream;\nConnection: keep-alive\nCache-Control: no-cache\nAccess-Control-Allow-Origin: *\n\n"));
119+
s.keepAliveTimer.attach_scheduled(30.0, SSEKeepAlive); // Refresh time every 30s for demo
120+
}
121+
122+
void handleAll() {
123+
const char *uri = server.uri().c_str();
124+
const char *restEvents = PSTR("/rest/events/");
125+
if (strncmp_P(uri, restEvents, strlen_P(restEvents))) {
126+
return handleNotFound();
127+
}
128+
uri += strlen_P(restEvents); // Skip the "/rest/events/" and get to the channel number
129+
unsigned int channel = atoi(uri);
130+
if (channel < SSE_MAX_CHANNELS) {
131+
return SSEHandler(channel);
132+
}
133+
handleNotFound();
134+
};
135+
136+
void SSEBroadcastState(const char *sensorName, unsigned short prevSensorValue, unsigned short sensorValue) {
137+
for (uint8_t i = 0; i < SSE_MAX_CHANNELS; i++) {
138+
if (!(subscription[i].clientIP)) {
139+
continue;
140+
}
141+
String IPaddrstr = IPAddress(subscription[i].clientIP).toString();
142+
if (subscription[i].client.connected()) {
143+
Serial.printf_P(PSTR("broadcast status change to client IP %s on channel %d for %s with new state %d\n"),
144+
IPaddrstr.c_str(), i, sensorName, sensorValue);
145+
subscription[i].client.printf_P(PSTR("event: event\ndata: {\"TYPE\":\"STATE\", \"%s\":{\"state\":%d, \"prevState\":%d}}\n\n"),
146+
sensorName, sensorValue, prevSensorValue);
147+
} else {
148+
Serial.printf_P(PSTR("SSEBroadcastState - client %s registered on channel %d but not listening\n"), IPaddrstr.c_str(), i);
149+
}
150+
}
151+
}
152+
153+
// Simulate sensors
154+
void updateSensor(sensorType &sensor) {
155+
unsigned short newVal = (unsigned short)RANDOM_REG32; // (not so good) random value for the sensor
156+
Serial.printf_P(PSTR("update sensor %s - previous state: %d, new state: %d\n"), sensor.name, sensor.value, newVal);
157+
if (sensor.value != newVal) {
158+
SSEBroadcastState(sensor.name, sensor.value, newVal); // only broadcast if state is different
159+
}
160+
sensor.value = newVal;
161+
sensor.update.once(rand() % 20 + 10, std::bind(updateSensor, sensor)); // randomly update sensor
162+
}
163+
164+
void handleSubscribe() {
165+
if (subscriptionCount == SSE_MAX_CHANNELS - 1) {
166+
return handleNotFound(); // We ran out of channels
167+
}
168+
169+
uint8_t channel;
170+
IPAddress clientIP = server.client().remoteIP(); // get IP address of client
171+
String SSEurl = F("http://");
172+
SSEurl += WiFi.localIP().toString();
173+
SSEurl += F(":");
174+
SSEurl += port;
175+
size_t offset = SSEurl.length();
176+
SSEurl += F("/rest/events/");
177+
178+
++subscriptionCount;
179+
for (channel = 0; channel < SSE_MAX_CHANNELS; channel++) // Find first free slot
180+
if (!subscription[channel].clientIP) {
181+
break;
182+
}
183+
subscription[channel] = {clientIP, server.client(), Ticker()};
184+
SSEurl += channel;
185+
Serial.printf_P(PSTR("Allocated channel %d, on uri %s\n"), channel, SSEurl.substring(offset).c_str());
186+
//server.on(SSEurl.substring(offset), std::bind(SSEHandler, &(subscription[channel])));
187+
Serial.printf_P(PSTR("subscription for client IP %s: event bus location: %s\n"), clientIP.toString().c_str(), SSEurl.c_str());
188+
server.send_P(200, "text/plain", SSEurl.c_str());
189+
}
190+
191+
void startServers() {
192+
server.on(F("/rest/events/subscribe"), handleSubscribe);
193+
server.onNotFound(handleAll);
194+
server.begin();
195+
Serial.println("HTTP server and SSE EventSource started");
196+
}
197+
198+
void setup(void) {
199+
Serial.begin(115200);
200+
WiFi.mode(WIFI_STA);
201+
WiFi.begin(ssid, password);
202+
Serial.println("");
203+
while (WiFi.status() != WL_CONNECTED) { // Wait for connection
204+
delay(500);
205+
Serial.print(".");
206+
}
207+
Serial.printf_P(PSTR("\nConnected to %s with IP address: %s\n"), ssid, WiFi.localIP().toString().c_str());
208+
if (MDNS.begin("esp8266")) {
209+
Serial.println("MDNS responder started");
210+
}
211+
212+
startServers(); // start web and SSE servers
213+
sensor[0].name = "sensorA";
214+
sensor[1].name = "sensorB";
215+
updateSensor(sensor[0]);
216+
updateSensor(sensor[1]);
217+
}
218+
219+
void loop(void) {
220+
server.handleClient();
221+
MDNS.update();
222+
yield();
223+
}

‎libraries/ESP8266WebServer/src/detail/mimetable.cpp

Lines changed: 84 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,48 +5,97 @@
55
namespace mime
66
{
77

8-
// Table of extension->MIME strings stored in PROGMEM, needs to be global due to GCC section typing rules
9-
const Entry mimeTable[maxType] PROGMEM =
8+
static const char kHtmlSuffix[] PROGMEM = ".html";
9+
static const char kHtmSuffix[] PROGMEM = ".htm";
10+
static const char kTxtSuffix[] PROGMEM = ".txt";
11+
#ifndef MIMETYPE_MINIMAL
12+
static const char kCssSuffix[] PROGMEM = ".css";
13+
static const char kJsSuffix[] PROGMEM = ".js";
14+
static const char kJsonSuffix[] PROGMEM = ".json";
15+
static const char kPngSuffix[] PROGMEM = ".png";
16+
static const char kGifSuffix[] PROGMEM = ".gif";
17+
static const char kJpgSuffix[] PROGMEM = ".jpg";
18+
static const char kJpegSuffix[] PROGMEM = ".jpeg";
19+
static const char kIcoSuffix[] PROGMEM = ".ico";
20+
static const char kSvgSuffix[] PROGMEM = ".svg";
21+
static const char kTtfSuffix[] PROGMEM = ".ttf";
22+
static const char kOtfSuffix[] PROGMEM = ".otf";
23+
static const char kWoffSuffix[] PROGMEM = ".woff";
24+
static const char kWoff2Suffix[] PROGMEM = ".woff2";
25+
static const char kEotSuffix[] PROGMEM = ".eot";
26+
static const char kSfntSuffix[] PROGMEM = ".sfnt";
27+
static const char kXmlSuffix[] PROGMEM = ".xml";
28+
static const char kPdfSuffix[] PROGMEM = ".pdf";
29+
static const char kZipSuffix[] PROGMEM = ".zip";
30+
static const char kAppcacheSuffix[] PROGMEM = ".appcache";
31+
#endif // MIMETYPE_MINIMAL
32+
static const char kGzSuffix[] PROGMEM = ".gz";
33+
static const char kDefaultSuffix[] PROGMEM = "";
34+
35+
static const char kHtml[] PROGMEM = "text/html";
36+
static const char kTxt[] PROGMEM = "text/plain";
37+
#ifndef MIMETYPE_MINIMAL
38+
static const char kCss[] PROGMEM = "text/css";
39+
static const char kJs[] PROGMEM = "application/javascript";
40+
static const char kJson[] PROGMEM = "application/json";
41+
static const char kPng[] PROGMEM = "image/png";
42+
static const char kGif[] PROGMEM = "image/gif";
43+
static const char kJpg[] PROGMEM = "image/jpeg";
44+
static const char kJpeg[] PROGMEM = "image/jpeg";
45+
static const char kIco[] PROGMEM = "image/x-icon";
46+
static const char kSvg[] PROGMEM = "image/svg+xml";
47+
static const char kTtf[] PROGMEM = "application/x-font-ttf";
48+
static const char kOtf[] PROGMEM = "application/x-font-opentype";
49+
static const char kWoff[] PROGMEM = "application/font-woff";
50+
static const char kWoff2[] PROGMEM = "application/font-woff2";
51+
static const char kEot[] PROGMEM = "application/vnd.ms-fontobject";
52+
static const char kSfnt[] PROGMEM = "application/font-sfnt";
53+
static const char kXml[] PROGMEM = "text/xml";
54+
static const char kPdf[] PROGMEM = "application/pdf";
55+
static const char kZip[] PROGMEM = "application/zip";
56+
static const char kAppcache[] PROGMEM = "text/cache-manifest";
57+
#endif // MIMETYPE_MINIMAL
58+
static const char kGz[] PROGMEM = "application/x-gzip";
59+
static const char kDefault[] PROGMEM = "application/octet-stream";
60+
61+
const Entry mimeTable[maxType] PROGMEM =
1062
{
11-
{ ".html", "text/html" },
12-
{ ".htm", "text/html" },
13-
{ ".css", "text/css" },
14-
{ ".txt", "text/plain" },
15-
{ ".js", "application/javascript" },
16-
{ ".json", "application/json" },
17-
{ ".png", "image/png" },
18-
{ ".gif", "image/gif" },
19-
{ ".jpg", "image/jpeg" },
20-
{ ".jpeg", "image/jpeg" },
21-
{ ".ico", "image/x-icon" },
22-
{ ".svg", "image/svg+xml" },
23-
{ ".ttf", "application/x-font-ttf" },
24-
{ ".otf", "application/x-font-opentype" },
25-
{ ".woff", "application/font-woff" },
26-
{ ".woff2", "application/font-woff2" },
27-
{ ".eot", "application/vnd.ms-fontobject" },
28-
{ ".sfnt", "application/font-sfnt" },
29-
{ ".xml", "text/xml" },
30-
{ ".pdf", "application/pdf" },
31-
{ ".zip", "application/zip" },
32-
{ ".gz", "application/x-gzip" },
33-
{ ".appcache", "text/cache-manifest" },
34-
{ "", "application/octet-stream" }
63+
{ kHtmlSuffix, kHtml },
64+
{ kHtmSuffix, kHtml },
65+
{ kTxtSuffix, kTxtSuffix },
66+
#ifndef MIMETYPE_MINIMAL
67+
{ kCssSuffix, kCss },
68+
{ kJsSuffix, kJs },
69+
{ kJsonSuffix, kJson },
70+
{ kPngSuffix, kPng },
71+
{ kGifSuffix, kGif },
72+
{ kJpgSuffix, kJpg },
73+
{ kJpegSuffix, kJpeg },
74+
{ kIcoSuffix, kIco },
75+
{ kSvgSuffix, kSvg },
76+
{ kTtfSuffix, kTtf },
77+
{ kOtfSuffix, kOtf },
78+
{ kWoffSuffix, kWoff },
79+
{ kWoff2Suffix, kWoff2 },
80+
{ kEotSuffix, kEot },
81+
{ kSfntSuffix, kSfnt },
82+
{ kXmlSuffix, kXml },
83+
{ kPdfSuffix, kPdf },
84+
{ kZipSuffix, kZip },
85+
{ kAppcacheSuffix, kAppcache },
86+
#endif // MIMETYPE_MINIMAL
87+
{ kGzSuffix, kGz },
88+
{ kDefaultSuffix, kDefault }
3589
};
3690

3791
String getContentType(const String& path) {
38-
char buff[sizeof(mimeTable[0].mimeType)];
39-
// Check all entries but last one for match, return if found
40-
for (size_t i=0; i < sizeof(mimeTable)/sizeof(mimeTable[0])-1; i++) {
41-
strcpy_P(buff, mimeTable[i].endsWith);
42-
if (path.endsWith(buff)) {
43-
strcpy_P(buff, mimeTable[i].mimeType);
44-
return String(buff);
92+
for (size_t i = 0; i < maxType; i++) {
93+
if (path.endsWith(FPSTR(mimeTable[i].endsWith))) {
94+
return String(FPSTR(mimeTable[i].mimeType));
4595
}
4696
}
4797
// Fall-through and just return default type
48-
strcpy_P(buff, mimeTable[sizeof(mimeTable)/sizeof(mimeTable[0])-1].mimeType);
49-
return String(buff);
98+
return String(FPSTR(kDefault));
5099
}
51100

52101
}

‎libraries/ESP8266WebServer/src/detail/mimetable.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ enum type
1010
{
1111
html,
1212
htm,
13-
css,
1413
txt,
14+
#ifndef MIMETYPE_MINIMAL // allow to compile with only the strict minimum of mime-types
15+
css,
1516
js,
1617
json,
1718
png,
@@ -29,16 +30,17 @@ enum type
2930
xml,
3031
pdf,
3132
zip,
32-
gz,
3333
appcache,
34+
#endif // MIMETYPE_MINIMAL
35+
gz,
3436
none,
3537
maxType
3638
};
3739

3840
struct Entry
3941
{
40-
const char endsWith[16];
41-
const char mimeType[32];
42+
const char * endsWith;
43+
const char * mimeType;
4244
};
4345

4446

‎libraries/ESP8266WiFi/src/BearSSLHelpers.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#define _BEARSSLHELPERS_H
2525

2626
#include <bearssl/bearssl.h>
27+
#include <StackThunk.h>
2728
#include <Updater.h>
2829

2930
// Internal opaque structures, not needed by user applications
@@ -157,7 +158,8 @@ class SigningVerifier : public UpdaterVerifyClass {
157158
virtual bool verify(UpdaterHashClass *hash, const void *signature, uint32_t signatureLen) override;
158159

159160
public:
160-
SigningVerifier(PublicKey *pubKey) { _pubKey = pubKey; }
161+
SigningVerifier(PublicKey *pubKey) { _pubKey = pubKey; stack_thunk_add_ref(); }
162+
~SigningVerifier() { stack_thunk_del_ref(); }
161163

162164
private:
163165
PublicKey *_pubKey;

0 commit comments

Comments
 (0)
Please sign in to comment.