Subversion Repositories ESP8266_P1_Meter

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 raymond 1
// SPDX-License-Identifier: LGPL-3.0-or-later
2
// Copyright 2016-2026 Hristo Gochkov, Mathieu Carbou, Emil Muratov, Will Miles
3
 
4
#pragma once
5
 
6
#include <Arduino.h>
7
 
8
#if defined(ESP32) || defined(LIBRETINY) || defined(HOST)
9
#include <AsyncTCP.h>
10
#ifdef LIBRETINY
11
#ifdef round
12
#undef round
13
#endif
14
#endif
15
#include <mutex>
16
#ifndef WS_MAX_QUEUED_MESSAGES
17
#define WS_MAX_QUEUED_MESSAGES 32
18
#endif
19
#elif defined(ESP8266)
20
#include <ESPAsyncTCP.h>
21
#ifndef WS_MAX_QUEUED_MESSAGES
22
#define WS_MAX_QUEUED_MESSAGES 8
23
#endif
24
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
25
#include <RPAsyncTCP.h>
26
#ifndef WS_MAX_QUEUED_MESSAGES
27
#define WS_MAX_QUEUED_MESSAGES 32
28
#endif
29
#endif
30
 
31
#include <ESPAsyncWebServer.h>
32
#include <AsyncWebServerLogging.h>
33
 
34
#include <cstdio>
35
#include <deque>
36
#include <list>
37
#include <memory>
38
 
39
#if defined(ESP8266) || defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
40
#include <Hash.h>
41
#ifdef CRYPTO_HASH_h  // include Hash.h from espressif framework if the first include was from the crypto library
42
#include <../src/Hash.h>
43
#endif
44
#endif
45
 
46
#ifndef DEFAULT_MAX_WS_CLIENTS
47
#if defined(ESP32) || defined(HOST)
48
#define DEFAULT_MAX_WS_CLIENTS 8
49
#else
50
#define DEFAULT_MAX_WS_CLIENTS 4
51
#endif
52
#endif
53
 
54
using AsyncWebSocketSharedBuffer = std::shared_ptr<std::vector<uint8_t>>;
55
 
56
class AsyncWebSocket;
57
class AsyncWebSocketResponse;
58
class AsyncWebSocketClient;
59
 
60
/*
61
 * Control Frame
62
 */
63
 
64
class AsyncWebSocketControl {
65
private:
66
  uint8_t _opcode;
67
  uint8_t *_data;
68
  size_t _len;
69
  bool _mask;
70
  bool _finished;
71
 
72
public:
73
  AsyncWebSocketControl(uint8_t opcode, const uint8_t *data = NULL, size_t len = 0, bool mask = false)
74
    : _opcode(opcode), _len(len), _mask(len && mask), _finished(false) {
75
    if (data == NULL) {
76
      _len = 0;
77
    }
78
    if (_len) {
79
      if (_len > 125) {
80
        _len = 125;
81
      }
82
 
83
      _data = (uint8_t *)malloc(_len);
84
 
85
      if (_data == NULL) {
86
        async_ws_log_e("Failed to allocate");
87
        _len = 0;
88
      } else {
89
        memcpy(_data, data, len);
90
      }
91
    } else {
92
      _data = NULL;
93
    }
94
  }
95
 
96
  ~AsyncWebSocketControl() {
97
    if (_data != NULL) {
98
      free(_data);
99
    }
100
  }
101
 
102
  bool finished() const {
103
    return _finished;
104
  }
105
  uint8_t opcode() {
106
    return _opcode;
107
  }
108
  uint8_t len() {
109
    return _len + 2;
110
  }
111
  size_t send(AsyncClient *client);
112
};
113
 
114
typedef struct {
115
  /** Message type as defined by enum AwsFrameType.
116
     * Note: Applications will only see WS_TEXT and WS_BINARY.
117
     * All other types are handled by the library. */
118
  uint8_t message_opcode;
119
  /** Frame number of a fragmented message. */
120
  uint32_t num;
121
  /** Is this the last frame in a fragmented message ?*/
122
  uint8_t final;
123
  /** Is this frame masked? */
124
  uint8_t masked;
125
  /** Message type as defined by enum AwsFrameType.
126
     * This value is the same as message_opcode for non-fragmented
127
     * messages, but may also be WS_CONTINUATION in a fragmented message. */
128
  uint8_t opcode;
129
  /** Length of the current frame.
130
     * This equals the total length of the message if num == 0 && final == true */
131
  uint64_t len;
132
  /** Mask key */
133
  uint8_t mask[4];
134
  /** Offset of the data inside the current frame. */
135
  uint64_t index;
136
} AwsFrameInfo;
137
 
138
typedef enum {
139
  WS_DISCONNECTED,
140
  WS_CONNECTED,
141
  WS_DISCONNECTING
142
} AwsClientStatus;
143
typedef enum {
144
  WS_CONTINUATION,
145
  WS_TEXT,
146
  WS_BINARY,
147
  WS_DISCONNECT = 0x08,
148
  WS_PING,
149
  WS_PONG
150
} AwsFrameType;
151
typedef enum {
152
  WS_MSG_SENDING,
153
  WS_MSG_SENT,
154
  WS_MSG_ERROR
155
} AwsMessageStatus;
156
typedef enum {
157
  WS_EVT_CONNECT,
158
  WS_EVT_DISCONNECT,
159
  WS_EVT_PING,
160
  WS_EVT_PONG,
161
  WS_EVT_ERROR,
162
  WS_EVT_DATA
163
} AwsEventType;
164
 
165
class AsyncWebSocketMessageBuffer {
166
  friend AsyncWebSocket;
167
  friend AsyncWebSocketClient;
168
 
169
private:
170
  AsyncWebSocketSharedBuffer _buffer;
171
 
172
public:
173
  AsyncWebSocketMessageBuffer() {}
174
  explicit AsyncWebSocketMessageBuffer(size_t size);
175
  AsyncWebSocketMessageBuffer(const uint8_t *data, size_t size);
176
  //~AsyncWebSocketMessageBuffer();
177
  bool reserve(size_t size);
178
  uint8_t *get() {
179
    return _buffer->data();
180
  }
181
  size_t length() const {
182
    return _buffer->size();
183
  }
184
};
185
 
186
class AsyncWebSocketMessage {
187
  friend AsyncWebSocketClient;
188
 
189
private:
190
  size_t _remainingBytesToSend() const {
191
    return _WSbuffer->size() - _sent;
192
  }
193
 
194
  AsyncWebSocketSharedBuffer _WSbuffer;
195
  uint8_t _opcode{WS_TEXT};
196
  bool _mask{false};
197
  AwsMessageStatus _status{WS_MSG_ERROR};
198
  size_t _sent{};
199
  size_t _ack{};
200
  size_t _acked{};
201
 
202
public:
203
  AsyncWebSocketMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false);
204
 
205
  bool finished() const {
206
    return _status != WS_MSG_SENDING;
207
  }
208
  bool betweenFrames() const {
209
    return _acked == _ack;
210
  }
211
 
212
  size_t ack(size_t len, uint32_t time);
213
  size_t send(AsyncClient *client);
214
};
215
 
216
class AsyncWebSocketClient {
217
private:
218
  AsyncClient *_client;
219
  AsyncWebSocket *_server;
220
  uint32_t _clientId;
221
  AwsClientStatus _status;
222
  uint8_t _pstate;
223
  uint32_t _lastMessageTime;
224
  uint32_t _keepAlivePeriod;
225
  mutable asyncsrv::mutex_type _queue_lock;
226
  std::deque<AsyncWebSocketControl> _controlQueue;
227
  std::deque<AsyncWebSocketMessage> _messageQueue;
228
  bool _closeWhenFull = false;
229
 
230
  AwsFrameInfo _pinfo;
231
 
232
  bool _queueControl(uint8_t opcode, const uint8_t *data = NULL, size_t len = 0, bool mask = false);
233
  bool _queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false);
234
  void _runQueue();
235
  void _clearQueue();
236
 
237
  // this function is called when a text message is received, in order to copy the buffer and place a null terminator at the end of the buffer for easier handling of text messages.
238
  void _handleDataEvent(uint8_t *data, size_t len, bool endOfPaquet);
239
 
240
public:
241
  void *_tempObject;
242
 
243
  AsyncWebSocketClient(AsyncClient *client, AsyncWebSocket *server);
244
 
245
  /**
246
   * @brief Construct a new Async Web Socket Client object
247
   * @note constructor would take the ownership of of AsyncTCP's client pointer from `request` parameter and call delete on it!
248
   * @param request
249
   * @param server
250
   */
251
  AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server) : AsyncWebSocketClient(request->clientRelease(), server){};
252
  ~AsyncWebSocketClient();
253
 
254
  // client id increments for the given server
255
  uint32_t id() const {
256
    return _clientId;
257
  }
258
  AwsClientStatus status() const {
259
    return _status;
260
  }
261
  AsyncClient *client() {
262
    return _client;
263
  }
264
  const AsyncClient *client() const {
265
    return _client;
266
  }
267
  AsyncWebSocket *server() {
268
    return _server;
269
  }
270
  const AsyncWebSocket *server() const {
271
    return _server;
272
  }
273
  AwsFrameInfo const &pinfo() const {
274
    return _pinfo;
275
  }
276
 
277
  // CloseClientOnQueueFull:
278
  //
279
  // - If "true", the client will be closed if the message queue becomes full.
280
  // This is the default behavior in yubox-node-org, which is not silently discarding messages but instead closes the connection.
281
  // The big issue with this behavior is  that is can cause the UI to automatically re-create a new WS connection, which can be filled again,
282
  // and so on, causing a resource exhaustion.
283
  // Also this can lead to a crash as explained in this issue: https://github.com/ESP32Async/ESPAsyncWebServer/issues/433
284
  //
285
  // - If "false" (default in this library), the incoming message will be discarded if the queue is full.
286
  // This is the default behavior in the original ESPAsyncWebServer library from me-no-dev.
287
  // This behavior allows the best performance at the expense of unreliable message delivery in case the queue is full (some messages may be lost).
288
  //
289
  // With recent refactorings of the library, the queue is barely used and the library supports a fast sending rate of messages. So if the queue is growing:
290
  //  - either the server is sending messages at an insane fast rate, faster than what the client can acknowledge, which can be the case if the client is slow or if the messages are big and the network is slow,
291
  //  - or there is a network issue causing the client to not receive messages, or network is broken. In that case, if the network is broken, the queue will fill temporarily until the connection is closed and client removed.
292
  //
293
  // In case your application requires a fast and high frequency message sending and you foresee some queue usage, you can:
294
  //  - increase the queue side to allow some room
295
  //  - check some functions status before or when sending in order to decrease your sending rate to let the queue drain, or take action by closing this client if necessary.
296
  //
297
  // This has to be an application-specific deicison that the library cannot take for you.
298
  // Here are a list of some functions that you can use and check the boolean value returned:
299
  // - the send methods
300
  // - queueIsFull()
301
  // - availableForWriteAll()
302
  // - availableForWrite(clientId)
303
  //
304
  // When the queue is full, a message is logged in case it is discarded.
305
  void setCloseClientOnQueueFull(bool close) {
306
    _closeWhenFull = close;
307
  }
308
  bool willCloseClientOnQueueFull() const {
309
    return _closeWhenFull;
310
  }
311
 
312
  IPAddress remoteIP() const;
313
  uint16_t remotePort() const;
314
 
315
  bool shouldBeDeleted() const {
316
    return !_client;
317
  }
318
 
319
  // control frames
320
  void close(uint16_t code = 0, const char *message = NULL);
321
  bool ping(const uint8_t *data = NULL, size_t len = 0);
322
 
323
  // set auto-ping period in seconds. disabled if zero (default)
324
  void keepAlivePeriod(uint16_t seconds) {
325
    _keepAlivePeriod = seconds * 1000;
326
  }
327
  uint16_t keepAlivePeriod() {
328
    return (uint16_t)(_keepAlivePeriod / 1000);
329
  }
330
 
331
  // data packets
332
  bool message(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false) {
333
    return _queueMessage(buffer, opcode, mask);
334
  }
335
  bool queueIsFull() const;
336
  size_t queueLen() const;
337
 
338
  size_t printf(const char *format, ...) __attribute__((format(printf, 2, 3)));
339
 
340
  bool text(AsyncWebSocketSharedBuffer buffer);
341
  bool text(const uint8_t *message, size_t len);
342
  bool text(const char *message, size_t len);
343
  bool text(const char *message);
344
  bool text(const String &message);
345
  bool text(AsyncWebSocketMessageBuffer *buffer);
346
 
347
  bool binary(AsyncWebSocketSharedBuffer buffer);
348
  bool binary(const uint8_t *message, size_t len);
349
  bool binary(const char *message, size_t len);
350
  bool binary(const char *message);
351
  bool binary(const String &message);
352
  bool binary(AsyncWebSocketMessageBuffer *buffer);
353
 
354
  bool canSend() const;
355
 
356
  // system callbacks (do not call)
357
  void _onAck(size_t len, uint32_t time);
358
  void _onError(int8_t);
359
  void _onPoll();
360
  void _onTimeout(uint32_t time);
361
  void _onDisconnect();
362
  void _onData(void *pbuf, size_t plen);
363
 
364
#ifdef ESP8266
365
  size_t printf_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3)));
366
  bool text(const __FlashStringHelper *message);
367
  bool binary(const __FlashStringHelper *message, size_t len);
368
#endif
369
};
370
 
371
using AwsHandshakeHandler = std::function<bool(AsyncWebServerRequest *request)>;
372
using AwsEventHandler = std::function<void(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len)>;
373
 
374
// WebServer Handler implementation that plays the role of a socket server
375
class AsyncWebSocket : public AsyncWebHandler {
376
private:
377
  String _url;
378
  std::list<AsyncWebSocketClient> _clients;
379
  uint32_t _cNextId;
380
  AwsEventHandler _eventHandler;
381
  AwsHandshakeHandler _handshakeHandler;
382
  bool _enabled;
383
  mutable asyncsrv::mutex_type _ws_clients_lock;
384
 
385
public:
386
  typedef enum {
387
    DISCARDED = 0,
388
    ENQUEUED = 1,
389
    PARTIALLY_ENQUEUED = 2,
390
  } SendStatus;
391
 
392
  explicit AsyncWebSocket(const char *url, AwsEventHandler handler = nullptr) : _url(url), _cNextId(1), _eventHandler(handler), _enabled(true) {}
393
  AsyncWebSocket(const String &url, AwsEventHandler handler = nullptr) : _url(url), _cNextId(1), _eventHandler(handler), _enabled(true) {}
394
  ~AsyncWebSocket(){};
395
  const char *url() const {
396
    return _url.c_str();
397
  }
398
  void enable(bool e) {
399
    _enabled = e;
400
  }
401
  bool enabled() const {
402
    return _enabled;
403
  }
404
  bool availableForWriteAll();
405
  bool availableForWrite(uint32_t id);
406
 
407
  size_t count() const;
408
  AsyncWebSocketClient *client(uint32_t id);
409
  bool hasClient(uint32_t id) {
410
    return client(id) != nullptr;
411
  }
412
 
413
  void close(uint32_t id, uint16_t code = 0, const char *message = NULL);
414
  void closeAll(uint16_t code = 0, const char *message = NULL);
415
  void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS);
416
 
417
  bool ping(uint32_t id, const uint8_t *data = NULL, size_t len = 0);
418
  SendStatus pingAll(const uint8_t *data = NULL, size_t len = 0);  //  done
419
 
420
  bool text(uint32_t id, const uint8_t *message, size_t len);
421
  bool text(uint32_t id, const char *message, size_t len);
422
  bool text(uint32_t id, const char *message);
423
  bool text(uint32_t id, const String &message);
424
  bool text(uint32_t id, AsyncWebSocketMessageBuffer *buffer);
425
  bool text(uint32_t id, AsyncWebSocketSharedBuffer buffer);
426
 
427
  SendStatus textAll(const uint8_t *message, size_t len);
428
  SendStatus textAll(const char *message, size_t len);
429
  SendStatus textAll(const char *message);
430
  SendStatus textAll(const String &message);
431
  SendStatus textAll(AsyncWebSocketMessageBuffer *buffer);
432
  SendStatus textAll(AsyncWebSocketSharedBuffer buffer);
433
 
434
  bool binary(uint32_t id, const uint8_t *message, size_t len);
435
  bool binary(uint32_t id, const char *message, size_t len);
436
  bool binary(uint32_t id, const char *message);
437
  bool binary(uint32_t id, const String &message);
438
  bool binary(uint32_t id, AsyncWebSocketMessageBuffer *buffer);
439
  bool binary(uint32_t id, AsyncWebSocketSharedBuffer buffer);
440
 
441
  SendStatus binaryAll(const uint8_t *message, size_t len);
442
  SendStatus binaryAll(const char *message, size_t len);
443
  SendStatus binaryAll(const char *message);
444
  SendStatus binaryAll(const String &message);
445
  SendStatus binaryAll(AsyncWebSocketMessageBuffer *buffer);
446
  SendStatus binaryAll(AsyncWebSocketSharedBuffer buffer);
447
 
448
  size_t printf(uint32_t id, const char *format, ...) __attribute__((format(printf, 3, 4)));
449
  size_t printfAll(const char *format, ...) __attribute__((format(printf, 2, 3)));
450
 
451
#ifdef ESP8266
452
  bool text(uint32_t id, const __FlashStringHelper *message);
453
  SendStatus textAll(const __FlashStringHelper *message);
454
  bool binary(uint32_t id, const __FlashStringHelper *message, size_t len);
455
  SendStatus binaryAll(const __FlashStringHelper *message, size_t len);
456
  size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__((format(printf, 3, 4)));
457
  size_t printfAll_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3)));
458
#endif
459
 
460
  void onEvent(AwsEventHandler handler) {
461
    _eventHandler = handler;
462
  }
463
  void handleHandshake(AwsHandshakeHandler handler) {
464
    _handshakeHandler = handler;
465
  }
466
 
467
  // system callbacks (do not call)
468
  uint32_t _getNextId() {
469
    return _cNextId++;
470
  }
471
  AsyncWebSocketClient *_newClient(AsyncWebServerRequest *request);
472
  void _handleDisconnect(AsyncWebSocketClient *client);
473
  void _handleEvent(AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len);
474
  bool canHandle(AsyncWebServerRequest *request) const final;
475
  void handleRequest(AsyncWebServerRequest *request) final;
476
 
477
  //  messagebuffer functions/objects.
478
  AsyncWebSocketMessageBuffer *makeBuffer(size_t size = 0);
479
  AsyncWebSocketMessageBuffer *makeBuffer(const uint8_t *data, size_t size);
480
 
481
  std::list<AsyncWebSocketClient> &getClients() {
482
    return _clients;
483
  }
484
};
485
 
486
// WebServer response to authenticate the socket and detach the tcp client from the web server request
487
class AsyncWebSocketResponse : public AsyncWebServerResponse {
488
private:
489
  String _content;
490
  AsyncWebSocket *_server;
491
  AsyncWebServerRequest *_request;
492
  // this call back will switch AsyncTCP client to WebSocket
493
  void _switchClient();
494
 
495
public:
496
  AsyncWebSocketResponse(const String &key, AsyncWebSocket *server);
497
  void _respond(AsyncWebServerRequest *request) override;
498
  size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time) override {
499
    return 0;
500
  };
501
  bool _sourceValid() const override {
502
    return true;
503
  }
504
};
505
 
506
class AsyncWebSocketMessageHandler {
507
public:
508
  AwsEventHandler eventHandler() const {
509
    return _handler;
510
  }
511
 
512
  void onConnect(std::function<void(AsyncWebSocket *server, AsyncWebSocketClient *client)> onConnect) {
513
    _onConnect = onConnect;
514
  }
515
 
516
  void onDisconnect(std::function<void(AsyncWebSocket *server, uint32_t clientId)> onDisconnect) {
517
    _onDisconnect = onDisconnect;
518
  }
519
 
520
  /**
521
   * Error callback
522
   * @param reason null-terminated string
523
   * @param len length of the string
524
   */
525
  void onError(std::function<void(AsyncWebSocket *server, AsyncWebSocketClient *client, uint16_t errorCode, const char *reason, size_t len)> onError) {
526
    _onError = onError;
527
  }
528
 
529
  /**
530
   * Complete message callback
531
   * @param data pointer to the data (binary or null-terminated string). This handler expects the user to know which data type he uses.
532
   */
533
  void onMessage(std::function<void(AsyncWebSocket *server, AsyncWebSocketClient *client, const uint8_t *data, size_t len)> onMessage) {
534
    _onMessage = onMessage;
535
  }
536
 
537
  /**
538
   * Fragmented message callback
539
   * @param data pointer to the data (binary or null-terminated string), will be null-terminated. This handler expects the user to know which data type he uses.
540
   */
541
  // clang-format off
542
  void onFragment(std::function<void(AsyncWebSocket *server, AsyncWebSocketClient *client, const AwsFrameInfo *frameInfo, const uint8_t *data, size_t len)> onFragment) {
543
    _onFragment = onFragment;
544
  }
545
  // clang-format on
546
 
547
private:
548
  // clang-format off
549
  std::function<void(AsyncWebSocket *server, AsyncWebSocketClient *client)> _onConnect;
550
  std::function<void(AsyncWebSocket *server, AsyncWebSocketClient *client, uint16_t errorCode, const char *reason, size_t len)> _onError;
551
  std::function<void(AsyncWebSocket *server, AsyncWebSocketClient *client, const uint8_t *data, size_t len)> _onMessage;
552
  std::function<void(AsyncWebSocket *server, AsyncWebSocketClient *client, const AwsFrameInfo *frameInfo, const uint8_t *data, size_t len)> _onFragment;
553
  std::function<void(AsyncWebSocket *server, uint32_t clientId)> _onDisconnect;
554
  // clang-format on
555
 
556
  // this handler is meant to only support 1-frame messages (== unfragmented messages)
557
  AwsEventHandler _handler = [this](AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
558
    if (type == WS_EVT_CONNECT) {
559
      if (_onConnect) {
560
        _onConnect(server, client);
561
      }
562
    } else if (type == WS_EVT_DISCONNECT) {
563
      if (_onDisconnect) {
564
        _onDisconnect(server, client->id());
565
      }
566
    } else if (type == WS_EVT_ERROR) {
567
      if (_onError) {
568
        _onError(server, client, *((uint16_t *)arg), (const char *)data, len);
569
      }
570
    } else if (type == WS_EVT_DATA) {
571
      AwsFrameInfo *info = (AwsFrameInfo *)arg;
572
      if (info->final && info->index == 0 && info->len == len) {
573
        if (_onMessage) {
574
          _onMessage(server, client, data, len);
575
        }
576
      } else {
577
        if (_onFragment) {
578
          _onFragment(server, client, info, data, len);
579
        }
580
      }
581
    }
582
  };
583
};