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
#include "AsyncWebSocket.h"
5
#include "AsyncWebServerLogging.h"
6
 
7
#include <libb64/cencode.h>
8
 
9
#if defined(ESP32)
10
#if ESP_IDF_VERSION_MAJOR < 5
11
#include "BackPort_SHA1Builder.h"
12
#else
13
#include <SHA1Builder.h>
14
#endif
15
#include <rom/ets_sys.h>
16
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) || defined(ESP8266)
17
#include <Hash.h>
18
#elif defined(LIBRETINY)
19
#include <mbedtls/sha1.h>
20
#elif defined(HOST)
21
#include "BackPort_SHA1Builder.h"
22
#ifndef FPSTR
23
#define FPSTR (const char *)
24
#endif
25
#endif
26
 
27
#include <algorithm>
28
#include <cstdio>
29
#include <cstring>
30
#include <memory>
31
#include <utility>
32
#include <cstdarg>
33
 
34
#define STATE_FRAME_START 0
35
#define STATE_FRAME_MASK  1
36
#define STATE_FRAME_DATA  2
37
 
38
using namespace asyncsrv;
39
 
40
size_t webSocketSendFrameWindow(AsyncClient *client) {
41
  if (!client || !client->canSend()) {
42
    return 0;
43
  }
44
  size_t space = client->space();
45
  if (space < 9) {
46
    return 0;
47
  }
48
  return space - 8;
49
}
50
 
51
size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool mask, uint8_t *data, size_t len) {
52
  if (!client || !client->canSend()) {
53
    // Serial.println("SF 1");
54
    return 0;
55
  }
56
  size_t space = client->space();
57
  if (space < 2) {
58
    // Serial.println("SF 2");
59
    return 0;
60
  }
61
  uint8_t mbuf[4] = {0, 0, 0, 0};
62
  uint8_t headLen = 2;
63
  if (len && mask) {
64
    headLen += 4;
65
    mbuf[0] = rand() % 0xFF;  // NOLINT(runtime/threadsafe_fn)
66
    mbuf[1] = rand() % 0xFF;  // NOLINT(runtime/threadsafe_fn)
67
    mbuf[2] = rand() % 0xFF;  // NOLINT(runtime/threadsafe_fn)
68
    mbuf[3] = rand() % 0xFF;  // NOLINT(runtime/threadsafe_fn)
69
  }
70
  if (len > 125) {
71
    headLen += 2;
72
  }
73
  if (space < headLen) {
74
    // Serial.println("SF 2");
75
    return 0;
76
  }
77
  space -= headLen;
78
 
79
  if (len > space) {
80
    len = space;
81
  }
82
 
83
  uint8_t *buf = (uint8_t *)malloc(headLen);
84
  if (buf == NULL) {
85
    async_ws_log_e("Failed to allocate");
86
    client->abort();
87
    return 0;
88
  }
89
 
90
  buf[0] = opcode & 0x0F;
91
  if (final) {
92
    buf[0] |= 0x80;
93
  }
94
  if (len < 126) {
95
    buf[1] = len & 0x7F;
96
  } else {
97
    buf[1] = 126;
98
    buf[2] = (uint8_t)((len >> 8) & 0xFF);
99
    buf[3] = (uint8_t)(len & 0xFF);
100
  }
101
  if (len && mask) {
102
    buf[1] |= 0x80;
103
    memcpy(buf + (headLen - 4), mbuf, 4);
104
  }
105
  if (client->add((const char *)buf, headLen) != headLen) {
106
    // os_printf("error adding %lu header bytes\n", headLen);
107
    free(buf);
108
    // Serial.println("SF 4");
109
    return 0;
110
  }
111
  free(buf);
112
 
113
  if (len) {
114
    if (len && mask) {
115
      size_t i;
116
      for (i = 0; i < len; i++) {
117
        data[i] = data[i] ^ mbuf[i % 4];
118
      }
119
    }
120
    if (client->add((const char *)data, len) != len) {
121
      // os_printf("error adding %lu data bytes\n", len);
122
      //  Serial.println("SF 5");
123
      return 0;
124
    }
125
  }
126
  if (!client->send()) {
127
    // os_printf("error sending frame: %lu\n", headLen+len);
128
    //  Serial.println("SF 6");
129
    return 0;
130
  }
131
  // Serial.println("SF");
132
  return len;
133
}
134
 
135
size_t AsyncWebSocketControl::send(AsyncClient *client) {
136
  _finished = true;
137
  return webSocketSendFrame(client, true, _opcode & 0x0F, _mask, _data, _len);
138
}
139
 
140
/*
141
 *    AsyncWebSocketMessageBuffer
142
 */
143
 
144
AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const uint8_t *data, size_t size) : _buffer(std::make_shared<std::vector<uint8_t>>(size)) {
145
  if (_buffer->capacity() < size) {
146
    _buffer->reserve(size);
147
  } else {
148
    std::memcpy(_buffer->data(), data, size);
149
  }
150
}
151
 
152
AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size) : _buffer(std::make_shared<std::vector<uint8_t>>(size)) {
153
  if (_buffer->capacity() < size) {
154
    _buffer->reserve(size);
155
  }
156
}
157
 
158
bool AsyncWebSocketMessageBuffer::reserve(size_t size) {
159
  if (_buffer->capacity() >= size) {
160
    return true;
161
  }
162
  _buffer->reserve(size);
163
  return _buffer->capacity() >= size;
164
}
165
 
166
/*
167
 * AsyncWebSocketMessage Message
168
 */
169
 
170
AsyncWebSocketMessage::AsyncWebSocketMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode, bool mask)
171
  : _WSbuffer{buffer}, _opcode(opcode & 0x07), _mask{mask}, _status{_WSbuffer ? WS_MSG_SENDING : WS_MSG_ERROR} {}
172
 
173
size_t AsyncWebSocketMessage::ack(size_t len, uint32_t time) {
174
  (void)time;
175
  const size_t pending = std::min(len, _ack - _acked);
176
  _acked += pending;
177
  if (_sent >= _WSbuffer->size() && _acked >= _ack) {
178
    _status = WS_MSG_SENT;
179
  }
180
  const size_t remaining = len - pending;
181
  async_ws_log_v("ACK[%" PRIu8 "] %u/%u (acked: %u/%u) => %" PRIu8, _opcode, _sent, _WSbuffer->size(), _acked, _ack, static_cast<uint8_t>(_status));
182
  return remaining;
183
}
184
 
185
size_t AsyncWebSocketMessage::send(AsyncClient *client) {
186
  if (!client) {
187
    async_ws_log_v("No client");
188
    return 0;
189
  }
190
 
191
  if (_status != WS_MSG_SENDING) {
192
    async_ws_log_v("SEND[%" PRIu8 "] => [%" PRIu16 "] WS_MSG_SENDING != %" PRIu8, _opcode, client->remotePort(), static_cast<uint8_t>(_status));
193
    return 0;
194
  }
195
 
196
  if (_sent == _WSbuffer->size()) {
197
    if (_acked == _ack) {
198
      _status = WS_MSG_SENT;
199
    }
200
    async_ws_log_v("SEND[%" PRIu8 "] => [%" PRIu16 "] WS_MSG_SENT %u/%u (acked: %u/%u)", _opcode, client->remotePort(), _sent, _WSbuffer->size(), _acked, _ack);
201
    return 0;
202
  }
203
  if (_sent > _WSbuffer->size()) {
204
    _status = WS_MSG_ERROR;
205
    async_ws_log_v(
206
      "SEND[%" PRIu8 "] => [%" PRIu16 "] WS_MSG_ERROR %u/%u (acked: %u/%u)", _opcode, client->remotePort(), _sent, _WSbuffer->size(), _acked, _ack
207
    );
208
    return 0;
209
  }
210
 
211
  size_t toSend = _WSbuffer->size() - _sent;
212
  const size_t window = webSocketSendFrameWindow(client);
213
 
214
  // not enough space in lwip buffer ?
215
  if (!window) {
216
    async_ws_log_v("SEND[%" PRIu8 "] => [%" PRIu16 "] NO_SPACE %u", _opcode, client->remotePort(), toSend);
217
    return 0;
218
  }
219
 
220
  toSend = std::min(toSend, window);
221
 
222
  _sent += toSend;
223
  _ack += toSend + ((toSend < 126) ? 2 : 4) + (_mask * 4);
224
 
225
  bool final = (_sent == _WSbuffer->size());
226
  uint8_t *dPtr = (uint8_t *)(_WSbuffer->data() + (_sent - toSend));
227
  uint8_t opCode = (toSend && _sent == toSend) ? _opcode : (uint8_t)WS_CONTINUATION;
228
 
229
  size_t sent = webSocketSendFrame(client, final, opCode, _mask, dPtr, toSend);
230
  _status = WS_MSG_SENDING;
231
  if (toSend && sent != toSend) {
232
    _sent -= (toSend - sent);
233
    _ack -= (toSend - sent);
234
  }
235
 
236
  async_ws_log_v(
237
    "SEND[%" PRIu8 "] => [%" PRIu16 "] WS_MSG_SENDING %u/%u (acked: %u/%u)", _opcode, client->remotePort(), _sent, _WSbuffer->size(), _acked, _ack
238
  );
239
  return sent;
240
}
241
 
242
/*
243
 * Async WebSocket Client
244
 */
245
const char *AWSC_PING_PAYLOAD = "ESPAsyncWebServer-PING";
246
const size_t AWSC_PING_PAYLOAD_LEN = 22;
247
 
248
AsyncWebSocketClient::AsyncWebSocketClient(AsyncClient *client, AsyncWebSocket *server)
249
  : _client(client), _server(server), _clientId(_server->_getNextId()), _status(WS_CONNECTED), _pstate(STATE_FRAME_START), _lastMessageTime(millis()),
250
    _keepAlivePeriod(0), _tempObject(NULL) {
251
 
252
  _client->setRxTimeout(0);
253
  _client->onError(
254
    [](void *r, AsyncClient *c, int8_t error) {
255
      (void)c;
256
      ((AsyncWebSocketClient *)(r))->_onError(error);
257
    },
258
    this
259
  );
260
  _client->onAck(
261
    [](void *r, AsyncClient *c, size_t len, uint32_t time) {
262
      (void)c;
263
      ((AsyncWebSocketClient *)(r))->_onAck(len, time);
264
    },
265
    this
266
  );
267
  _client->onDisconnect(
268
    [](void *r, AsyncClient *c) {
269
      ((AsyncWebSocketClient *)(r))->_onDisconnect();
270
      delete c;
271
    },
272
    this
273
  );
274
  _client->onTimeout(
275
    [](void *r, AsyncClient *c, uint32_t time) {
276
      (void)c;
277
      ((AsyncWebSocketClient *)(r))->_onTimeout(time);
278
    },
279
    this
280
  );
281
  _client->onData(
282
    [](void *r, AsyncClient *c, void *buf, size_t len) {
283
      (void)c;
284
      ((AsyncWebSocketClient *)(r))->_onData(buf, len);
285
    },
286
    this
287
  );
288
  _client->onPoll(
289
    [](void *r, AsyncClient *c) {
290
      (void)c;
291
      ((AsyncWebSocketClient *)(r))->_onPoll();
292
    },
293
    this
294
  );
295
  memset(&_pinfo, 0, sizeof(_pinfo));
296
}
297
 
298
AsyncWebSocketClient::~AsyncWebSocketClient() {
299
  {
300
    asyncsrv::lock_guard_type lock(_queue_lock);
301
    _messageQueue.clear();
302
    _controlQueue.clear();
303
  }
304
  _server->_handleEvent(this, WS_EVT_DISCONNECT, NULL, NULL, 0);
305
}
306
 
307
void AsyncWebSocketClient::_clearQueue() {
308
  while (!_messageQueue.empty() && _messageQueue.front().finished()) {
309
    _messageQueue.pop_front();
310
  }
311
}
312
 
313
void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) {
314
  _lastMessageTime = millis();
315
 
316
  asyncsrv::unique_lock_type lock(_queue_lock);
317
 
318
  async_ws_log_v("[%s][%" PRIu32 "] START ACK(%u, %" PRIu32 ") Q:%u", _server->url(), _clientId, len, time, _messageQueue.size());
319
 
320
  if (!_controlQueue.empty()) {
321
    auto &head = _controlQueue.front();
322
    if (head.finished()) {
323
      len -= head.len();
324
      if (_status == WS_DISCONNECTING && head.opcode() == WS_DISCONNECT) {
325
        _controlQueue.pop_front();
326
        _status = WS_DISCONNECTED;
327
        async_ws_log_v("[%s][%" PRIu32 "] ACK WS_DISCONNECTED", _server->url(), _clientId);
328
        // Capture _client before unlocking: _client->close() triggers the _onDisconnect() --> _handleDisconnect() --> ~AsyncWebSocketClient() chain,
329
        // so we must not access any member after unlock.
330
        AsyncClient *c = _client;
331
        if (c) {
332
          lock.unlock();
333
          c->close();
334
        }
335
        return;
336
      }
337
      _controlQueue.pop_front();
338
    }
339
  }
340
 
341
  if (len && !_messageQueue.empty()) {
342
    for (auto &msg : _messageQueue) {
343
      len = msg.ack(len, time);
344
      if (len == 0) {
345
        break;
346
      }
347
    }
348
  }
349
 
350
  _clearQueue();
351
 
352
  async_ws_log_v("[%s][%" PRIu32 "] END ACK(%u, %" PRIu32 ") Q:%u", _server->url(), _clientId, len, time, _messageQueue.size());
353
 
354
  _runQueue();
355
}
356
 
357
void AsyncWebSocketClient::_onPoll() {
358
  asyncsrv::unique_lock_type lock(_queue_lock);
359
 
360
  if (!_client) {
361
    return;
362
  }
363
 
364
  if (_client && _client->canSend() && (!_controlQueue.empty() || !_messageQueue.empty())) {
365
    _runQueue();
366
  } else if (_keepAlivePeriod > 0 && (millis() - _lastMessageTime) >= _keepAlivePeriod && (_controlQueue.empty() && _messageQueue.empty())) {
367
    lock.unlock();
368
    ping((uint8_t *)AWSC_PING_PAYLOAD, AWSC_PING_PAYLOAD_LEN);
369
  }
370
}
371
 
372
void AsyncWebSocketClient::_runQueue() {
373
  // all calls to this method MUST be protected by a mutex lock!
374
  if (!_client) {
375
    return;
376
  }
377
 
378
  _clearQueue();
379
 
380
  size_t space = webSocketSendFrameWindow(_client);
381
 
382
  if (space) {
383
    // control frames have priority over message frames
384
    // we can send a control frame if:
385
    // - there is no message frame in the queue, or the first message frame is between frames (all bytes sent are acked)
386
    // - the control frame is not finished (not sent yet)
387
    // - there is enough space to send the control frame (control frames are small, at most 129 bytes, so we can assume that if there is space to send it, it can be sent in one go)
388
    if (_messageQueue.empty() || _messageQueue.front().betweenFrames()) {
389
      for (auto &ctrl : _controlQueue) {
390
        if (ctrl.finished()) {
391
          continue;
392
        }
393
        if (space > (size_t)(ctrl.len() - 1)) {
394
          async_ws_log_v("[%s][%" PRIu32 "] SEND CTRL %" PRIu8, _server->url(), _clientId, ctrl.opcode());
395
          ctrl.send(_client);
396
          space = webSocketSendFrameWindow(_client);
397
        }
398
      }
399
    }
400
 
401
    // then we can send message frames if there is space
402
    if (space) {
403
      for (auto &msg : _messageQueue) {
404
        if (msg._remainingBytesToSend()) {
405
          async_ws_log_v(
406
            "[%s][%" PRIu32 "][%" PRIu8 "] SEND %u/%u (acked: %u/%u)", _server->url(), _clientId, msg._opcode, msg._sent, msg._WSbuffer->size(), msg._acked,
407
            msg._ack
408
          );
409
 
410
          // will use all the remaining space, or all the remaining bytes to send, whichever is smaller
411
          msg.send(_client);
412
          space = webSocketSendFrameWindow(_client);
413
 
414
          // If we haven't finished sending this message, we must stop here to preserve WebSocket ordering.
415
          // We can only pipeline subsequent messages if the current one is fully passed to TCP buffer.
416
          if (msg._remainingBytesToSend()) {
417
            async_ws_log_v("[%s][%" PRIu32 "][%" PRIu8 "] NO_SPACE", _server->url(), _clientId, msg._opcode);
418
            break;
419
          }
420
        } else if (!space) {
421
          // not enough space for another message
422
          async_ws_log_v("[%s][%" PRIu32 "] NO_SPACE", _server->url(), _clientId);
423
          break;
424
        }
425
      }
426
    }
427
  }
428
}
429
 
430
bool AsyncWebSocketClient::queueIsFull() const {
431
  asyncsrv::lock_guard_type lock(_queue_lock);
432
  return (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) || (_status != WS_CONNECTED);
433
}
434
 
435
size_t AsyncWebSocketClient::queueLen() const {
436
  asyncsrv::lock_guard_type lock(_queue_lock);
437
  return _messageQueue.size();
438
}
439
 
440
bool AsyncWebSocketClient::canSend() const {
441
  asyncsrv::lock_guard_type lock(_queue_lock);
442
  return _messageQueue.size() < WS_MAX_QUEUED_MESSAGES;
443
}
444
 
445
bool AsyncWebSocketClient::_queueControl(uint8_t opcode, const uint8_t *data, size_t len, bool mask) {
446
  asyncsrv::lock_guard_type lock(_queue_lock);
447
 
448
  if (!_client) {
449
    return false;
450
  }
451
 
452
  _controlQueue.emplace_back(opcode, data, len, mask);
453
  async_ws_log_v("[%s][%" PRIu32 "] QUEUE CTRL (%u) << %" PRIu8, _server->url(), _clientId, _controlQueue.size(), opcode);
454
 
455
  if (_client && _client->canSend()) {
456
    _runQueue();
457
  }
458
 
459
  return true;
460
}
461
 
462
bool AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode, bool mask) {
463
  asyncsrv::unique_lock_type lock(_queue_lock);
464
 
465
  if (!_client || !buffer || buffer->empty() || _status != WS_CONNECTED) {
466
    return false;
467
  }
468
 
469
  if (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) {
470
    if (_closeWhenFull) {
471
      _status = WS_DISCONNECTED;
472
 
473
      async_ws_log_w("[%s][%" PRIu32 "] Too many messages queued: closing connection", _server->url(), _clientId);
474
 
475
      // Capture _client before unlocking: _client->close() triggers the _onDisconnect() --> _handleDisconnect() --> ~AsyncWebSocketClient() chain,
476
      // so we must not access any member after unlock.
477
      AsyncClient *c = _client;
478
      if (c) {
479
        lock.unlock();
480
        c->close();
481
      }
482
 
483
    } else {
484
      async_ws_log_w("[%s][%" PRIu32 "] Too many messages queued: discarding new message", _server->url(), _clientId);
485
    }
486
 
487
    return false;
488
  }
489
 
490
  _messageQueue.emplace_back(buffer, opcode, mask);
491
  async_ws_log_v("[%s][%" PRIu32 "] QUEUE MSG (%u/%u) << %" PRIu8, _server->url(), _clientId, _messageQueue.size(), WS_MAX_QUEUED_MESSAGES, opcode);
492
 
493
  if (_client && _client->canSend()) {
494
    _runQueue();
495
  }
496
 
497
  return true;
498
}
499
 
500
void AsyncWebSocketClient::close(uint16_t code, const char *message) {
501
  if (_status != WS_CONNECTED) {
502
    return;
503
  }
504
 
505
  async_ws_log_w("[%s][%" PRIu32 "] CLOSE", _server->url(), _clientId);
506
 
507
  _status = WS_DISCONNECTING;
508
 
509
  if (code) {
510
    uint8_t packetLen = 2;
511
    if (message != NULL) {
512
      size_t mlen = strlen(message);
513
      if (mlen > 123) {
514
        mlen = 123;
515
      }
516
      packetLen += mlen;
517
    }
518
    char *buf = (char *)malloc(packetLen);
519
    if (buf != NULL) {
520
      buf[0] = (uint8_t)(code >> 8);
521
      buf[1] = (uint8_t)(code & 0xFF);
522
      if (message != NULL) {
523
        memcpy(buf + 2, message, packetLen - 2);
524
      }
525
      _queueControl(WS_DISCONNECT, (uint8_t *)buf, packetLen);
526
      free(buf);
527
      return;
528
    } else {
529
      async_ws_log_e("Failed to allocate");
530
      // Reads _client, then dereference it without any lock.
531
      // A concurrent _onDisconnect could null + delete the client between the check and the use.
532
      // Local capture ensures the pointer is read exactly once, eliminating the null-dereference.
533
      // (TOCTOU)
534
      AsyncClient *c = _client;
535
      if (c) {
536
        c->abort();
537
      }
538
    }
539
  }
540
  _queueControl(WS_DISCONNECT);
541
}
542
 
543
bool AsyncWebSocketClient::ping(const uint8_t *data, size_t len) {
544
  return _status == WS_CONNECTED && _queueControl(WS_PING, data, len);
545
}
546
 
547
void AsyncWebSocketClient::_onError(int8_t err) {
548
  async_ws_log_v("[%s][%" PRIu32 "] ERROR %" PRIi8, _server->url(), _clientId, static_cast<int8_t>(err));
549
}
550
 
551
void AsyncWebSocketClient::_onTimeout(uint32_t time) {
552
  // Reads _client, then dereference it without any lock.
553
  // A concurrent _onDisconnect could null + delete the client between the check and the use.
554
  // Local capture ensures the pointer is read exactly once, eliminating the null-dereference.
555
  // (TOCTOU)
556
  AsyncClient *c = _client;
557
  if (!c) {
558
    return;
559
  }
560
  async_ws_log_v("[%s][%" PRIu32 "] TIMEOUT %" PRIu32, _server->url(), _clientId, time);
561
  c->close();
562
}
563
 
564
void AsyncWebSocketClient::_onDisconnect() {
565
  async_ws_log_v("[%s][%" PRIu32 "] DISCONNECT", _server->url(), _clientId);
566
  _status = WS_DISCONNECTED;
567
  {
568
    // Every queue method (_queueControl, _queueMessage, _runQueue, _onPoll, _onAck) reads _client while holding _queue_lock.
569
    // For those guarded reads to be meaningful, the write must also be synchronized. This doesn't change _queue_lock's purpose — it still guards queue integrity — but ensures the "is client alive?" checks that protect queue operations see a consistent value.
570
    asyncsrv::lock_guard_type lock(_queue_lock);
571
    _client = nullptr;
572
  }
573
  _server->_handleDisconnect(this);
574
}
575
 
576
void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) {
577
  _lastMessageTime = millis();
578
  uint8_t *data = (uint8_t *)pbuf;
579
 
580
  while (plen > 0) {
581
    async_ws_log_v(
582
      "[%s][%" PRIu32 "] DATA plen: %" PRIu32 ", _pstate: %" PRIu8 ", _status: %" PRIu8, _server->url(), _clientId, static_cast<uint32_t>(plen), _pstate,
583
      static_cast<uint8_t>(_status)
584
    );
585
 
586
    if (_pstate == STATE_FRAME_START) {
587
      const uint8_t *fdata = data;
588
 
589
      _pinfo.index = 0;
590
      _pinfo.final = (fdata[0] & 0x80) != 0;
591
      _pinfo.opcode = fdata[0] & 0x0F;
592
      _pinfo.masked = ((fdata[1] & 0x80) != 0) ? 1 : 0;
593
      _pinfo.len = fdata[1] & 0x7F;
594
 
595
      data += 2;
596
      plen -= 2;
597
 
598
      if (_pinfo.len == 126 && plen >= 2) {
599
        _pinfo.len = fdata[3] | (uint16_t)(fdata[2]) << 8;
600
        data += 2;
601
        plen -= 2;
602
 
603
      } else if (_pinfo.len == 127 && plen >= 8) {
604
        _pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | (uint64_t)(fdata[5]) << 32
605
                     | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56;
606
        data += 8;
607
        plen -= 8;
608
      }
609
    }
610
 
611
    async_ws_log_v(
612
      "[%s][%" PRIu32 "] DATA _pinfo: index: %" PRIu64 ", final: %" PRIu8 ", opcode: %" PRIu8 ", masked: %" PRIu8 ", len: %" PRIu64, _server->url(), _clientId,
613
      _pinfo.index, _pinfo.final, _pinfo.opcode, _pinfo.masked, _pinfo.len
614
    );
615
 
616
    // Handle fragmented mask data - Safari may split the 4-byte mask across multiple packets
617
    // _pinfo.masked is 1 if we need to start reading mask bytes
618
    // _pinfo.masked is 2, 3, or 4 if we have partially read the mask
619
    // _pinfo.masked is 5 if the mask is complete
620
    while (_pinfo.masked && _pstate <= STATE_FRAME_MASK && _pinfo.masked < 5) {
621
      // check if we have some data
622
      if (plen == 0) {
623
        // Safari close frame edge case: masked bit set but no mask data
624
        if (_pinfo.opcode == WS_DISCONNECT) {
625
          async_ws_log_v("[%s][%" PRIu32 "] DATA close frame with incomplete mask, treating as unmasked", _server->url(), _clientId);
626
          _pinfo.masked = 0;
627
          _pinfo.index = 0;
628
          _pinfo.len = 0;
629
          _pstate = STATE_FRAME_START;
630
          break;
631
        }
632
 
633
        // wait for more data
634
        _pstate = STATE_FRAME_MASK;
635
        async_ws_log_v("[%s][%" PRIu32 "] DATA waiting for more mask data: read: %" PRIu8 "/4", _server->url(), _clientId, _pinfo.masked - 1);
636
        return;
637
      }
638
 
639
      // accumulate mask bytes
640
      _pinfo.mask[_pinfo.masked - 1] = data[0];
641
      data += 1;
642
      plen -= 1;
643
      _pinfo.masked++;
644
    }
645
 
646
    // all mask bytes read if we were reading them
647
    _pstate = STATE_FRAME_DATA;
648
 
649
    // restore masked to 1 for backward compatibility
650
    if (_pinfo.masked >= 5) {
651
      async_ws_log_v("[%s][%" PRIu32 "] DATA mask read complete", _server->url(), _clientId);
652
      _pinfo.masked = 1;
653
    }
654
 
655
    const size_t datalen = std::min((size_t)(_pinfo.len - _pinfo.index), plen);
656
 
657
    if (_pinfo.masked) {
658
      for (size_t i = 0; i < datalen; i++) {
659
        data[i] ^= _pinfo.mask[(_pinfo.index + i) % 4];
660
      }
661
    }
662
 
663
    if (_pinfo.index == 0) {  // first fragment of the frame
664
      // init message_opcode for this frame
665
      // note: For next WS_CONTINUATION frames, they have opcode 0, so message_opcode will stay like the first frame
666
      if (_pinfo.opcode == WS_TEXT || _pinfo.opcode == WS_BINARY) {
667
        _pinfo.message_opcode = _pinfo.opcode;
668
      }
669
      // init frame number to 0 if only 1 frame or if this is the first frame of a fragmented message
670
      if (_pinfo.final || datalen < _pinfo.len) {
671
        _pinfo.num = 0;
672
      }
673
    }
674
 
675
    if ((datalen + _pinfo.index) < _pinfo.len) {  // more fragments to read for this frame
676
      _pstate = STATE_FRAME_DATA;
677
 
678
      if (datalen > 0) {
679
        async_ws_log_v(
680
          "[%s][%" PRIu32 "] DATA processing next fragment of %s frame %" PRIu32 ", index: %" PRIu64 ", len: %" PRIu32 "", _server->url(), _clientId,
681
          (_pinfo.message_opcode == WS_TEXT) ? "text" : "binary", _pinfo.num, _pinfo.index, (uint32_t)datalen
682
        );
683
        _handleDataEvent(data, datalen, datalen == plen);  // datalen == plen means that we are processing the last part of the current TCP packet
684
      }
685
 
686
      // track index for next fragment
687
      _pinfo.index += datalen;
688
 
689
    } else if ((datalen + _pinfo.index) == _pinfo.len) {  // this is the last fragment for this frame
690
      _pstate = STATE_FRAME_START;
691
 
692
      if (_pinfo.opcode == WS_DISCONNECT) {
693
        async_ws_log_v("[%s][%" PRIu32 "] DATA WS_DISCONNECT", _server->url(), _clientId);
694
 
695
        if (datalen) {
696
          uint16_t reasonCode = (uint16_t)(data[0] << 8) + data[1];
697
          char *reasonString = (char *)(data + 2);
698
          if (reasonCode > 1001) {
699
            _server->_handleEvent(this, WS_EVT_ERROR, (void *)&reasonCode, (uint8_t *)reasonString, strlen(reasonString));
700
          }
701
        }
702
        if (_status == WS_DISCONNECTING) {
703
          _status = WS_DISCONNECTED;
704
          if (_client) {
705
            _client->close();
706
          }
707
        } else {
708
          _status = WS_DISCONNECTING;
709
          if (_client) {
710
            _client->ackLater();
711
          }
712
          _queueControl(WS_DISCONNECT, data, datalen);
713
        }
714
 
715
      } else if (_pinfo.opcode == WS_PING) {
716
        async_ws_log_v("[%s][%" PRIu32 "] DATA PING", _server->url(), _clientId);
717
        _server->_handleEvent(this, WS_EVT_PING, NULL, NULL, 0);
718
        _queueControl(WS_PONG, data, datalen);
719
 
720
      } else if (_pinfo.opcode == WS_PONG) {
721
        async_ws_log_v("[%s][%" PRIu32 "] DATA PONG", _server->url(), _clientId);
722
        if (datalen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0) {
723
          _server->_handleEvent(this, WS_EVT_PONG, NULL, NULL, 0);
724
        }
725
 
726
      } else if (_pinfo.opcode < WS_DISCONNECT) {  // continuation or text/binary frame
727
        async_ws_log_v(
728
          "[%s][%" PRIu32 "] DATA processing final fragment of %s frame %" PRIu32 ", index: %" PRIu64 ", len: %" PRIu32 "", _server->url(), _clientId,
729
          (_pinfo.message_opcode == WS_TEXT) ? "text" : "binary", _pinfo.num, _pinfo.index, (uint32_t)datalen
730
        );
731
 
732
        _handleDataEvent(data, datalen, datalen == plen);  // datalen == plen means that we are processing the last part of the current TCP packet
733
 
734
        if (_pinfo.final) {
735
          _pinfo.num = 0;
736
        } else {
737
          _pinfo.num += 1;
738
        }
739
      }
740
 
741
    } else {
742
      // unexpected frame error, close connection
743
      _pstate = STATE_FRAME_START;
744
 
745
      async_ws_log_v(
746
        "[%s][%" PRIu32 "] DATA frame error: len: %u, index: %" PRIu64 ", total: %" PRIu64 "\n", _server->url(), _clientId, datalen, _pinfo.index, _pinfo.len
747
      );
748
 
749
      _status = WS_DISCONNECTING;
750
      if (_client) {
751
        _client->ackLater();
752
      }
753
      _queueControl(WS_DISCONNECT, data, datalen);
754
      break;
755
    }
756
 
757
    data += datalen;
758
    plen -= datalen;
759
  }
760
}
761
 
762
void AsyncWebSocketClient::_handleDataEvent(uint8_t *data, size_t len, bool endOfPaquet) {
763
  // ------------------------------------------------------------
764
  // Issue 384: https://github.com/ESP32Async/ESPAsyncWebServer/issues/384
765
  // Discussion: https://github.com/ESP32Async/ESPAsyncWebServer/pull/383#discussion_r2760425739
766
  // The initial design of the library was doing a backup of the byte following the data buffer because the client code
767
  // was allowed and documented to do something like data[len] = 0; to facilitate null-terminated string handling.
768
  // This was a bit hacky but it was working and it was documented, although completely incorrect because it was modifying a byte outside of the data buffer.
769
  // So to fix this behavior and to avoid breaking existing client code that may be relying on this behavior, we now have to copy the data to a temporary buffer that has an extra byte for the null terminator.
770
  // ------------------------------------------------------------
771
  //
772
  // Optimization notes:
773
  //
774
  // 1) opcodes
775
  //
776
  // - info->opcode stores the current WS frame type (binary, text, continuation)
777
  // - info->message_opcode stores the WS frame type of the first frame of the message, which is used for fragmented messages to know the message type when processing subsequent frame with opcode 0 (continuation)
778
  // So we can use info->message_opcode to avoid copying the data for non-text frames, and only copy the data for text frames when we need to add a null terminator for client code convenience.
779
  //
780
  // 2) data copy vs data backup/restore
781
  // - endOfPaquet: is true when datalen == plen. plen is the remaining bytes in the current TCP packet, so if datalen == plen, it means that we are processing the last part of the current TCP packet.
782
  // In that case, we have to copy since we cannot backup/restore the byte after the data buffer.
783
  // Otherwise we can backup the byte and restore since we know that the byte after is owned by the current TCP packet (same pointer).
784
  if (_pinfo.message_opcode == WS_TEXT) {
785
    if (endOfPaquet) {
786
      std::unique_ptr<uint8_t[]> copy(new (std::nothrow) uint8_t[len + 1]());
787
      if (copy) {
788
        memcpy(copy.get(), data, len);
789
        copy[len] = 0;
790
        _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, copy.get(), len);
791
      } else {
792
        async_ws_log_e("Failed to allocate");
793
        if (_client) {
794
          _client->abort();
795
        }
796
      }
797
    } else {
798
      uint8_t backup = data[len];
799
      data[len] = 0;
800
      _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, len);
801
      data[len] = backup;
802
    }
803
  } else {
804
    _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, len);
805
  }
806
}
807
 
808
size_t AsyncWebSocketClient::printf(const char *format, ...) {
809
  va_list arg;
810
  va_start(arg, format);
811
  size_t len = vsnprintf(nullptr, 0, format, arg);
812
  va_end(arg);
813
 
814
  if (len == 0) {
815
    return 0;
816
  }
817
 
818
  char *buffer = new char[len + 1];
819
 
820
  if (!buffer) {
821
    return 0;
822
  }
823
 
824
  va_start(arg, format);
825
  len = vsnprintf(buffer, len + 1, format, arg);
826
  va_end(arg);
827
 
828
  bool enqueued = text(buffer, len);
829
  delete[] buffer;
830
  return enqueued ? len : 0;
831
}
832
 
833
#ifdef ESP8266
834
size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) {
835
  va_list arg;
836
  va_start(arg, formatP);
837
  size_t len = vsnprintf_P(nullptr, 0, formatP, arg);
838
  va_end(arg);
839
 
840
  if (len == 0) {
841
    return 0;
842
  }
843
 
844
  char *buffer = new char[len + 1];
845
 
846
  if (!buffer) {
847
    return 0;
848
  }
849
 
850
  va_start(arg, formatP);
851
  len = vsnprintf_P(buffer, len + 1, formatP, arg);
852
  va_end(arg);
853
 
854
  bool enqueued = text(buffer, len);
855
  delete[] buffer;
856
  return enqueued ? len : 0;
857
}
858
#endif
859
 
860
namespace {
861
AsyncWebSocketSharedBuffer makeSharedBuffer(const uint8_t *message, size_t len) {
862
  auto buffer = std::make_shared<std::vector<uint8_t>>(len);
863
  std::memcpy(buffer->data(), message, len);
864
  return buffer;
865
}
866
}  // namespace
867
 
868
bool AsyncWebSocketClient::text(AsyncWebSocketMessageBuffer *buffer) {
869
  bool enqueued = false;
870
  if (buffer) {
871
    enqueued = text(std::move(buffer->_buffer));
872
    delete buffer;
873
  }
874
  return enqueued;
875
}
876
 
877
bool AsyncWebSocketClient::text(AsyncWebSocketSharedBuffer buffer) {
878
  return _queueMessage(buffer);
879
}
880
 
881
bool AsyncWebSocketClient::text(const uint8_t *message, size_t len) {
882
  return text(makeSharedBuffer(message, len));
883
}
884
 
885
bool AsyncWebSocketClient::text(const char *message, size_t len) {
886
  return text((const uint8_t *)message, len);
887
}
888
 
889
bool AsyncWebSocketClient::text(const char *message) {
890
  return text(message, strlen(message));
891
}
892
 
893
bool AsyncWebSocketClient::text(const String &message) {
894
  return text(message.c_str(), message.length());
895
}
896
 
897
#ifdef ESP8266
898
bool AsyncWebSocketClient::text(const __FlashStringHelper *data) {
899
  PGM_P p = reinterpret_cast<PGM_P>(data);
900
 
901
  size_t n = 0;
902
  while (1) {
903
    if (pgm_read_byte(p + n) == 0) {
904
      break;
905
    }
906
    n += 1;
907
  }
908
 
909
  char *message = (char *)malloc(n + 1);
910
  bool enqueued = false;
911
  if (message) {
912
    memcpy_P(message, p, n);
913
    message[n] = 0;
914
    enqueued = text(message, n);
915
    free(message);
916
  }
917
  return enqueued;
918
}
919
#endif  // ESP8266
920
 
921
bool AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer *buffer) {
922
  bool enqueued = false;
923
  if (buffer) {
924
    enqueued = binary(std::move(buffer->_buffer));
925
    delete buffer;
926
  }
927
  return enqueued;
928
}
929
 
930
bool AsyncWebSocketClient::binary(AsyncWebSocketSharedBuffer buffer) {
931
  return _queueMessage(buffer, WS_BINARY);
932
}
933
 
934
bool AsyncWebSocketClient::binary(const uint8_t *message, size_t len) {
935
  return binary(makeSharedBuffer(message, len));
936
}
937
 
938
bool AsyncWebSocketClient::binary(const char *message, size_t len) {
939
  return binary((const uint8_t *)message, len);
940
}
941
 
942
bool AsyncWebSocketClient::binary(const char *message) {
943
  return binary(message, strlen(message));
944
}
945
 
946
bool AsyncWebSocketClient::binary(const String &message) {
947
  return binary(message.c_str(), message.length());
948
}
949
 
950
#ifdef ESP8266
951
bool AsyncWebSocketClient::binary(const __FlashStringHelper *data, size_t len) {
952
  PGM_P p = reinterpret_cast<PGM_P>(data);
953
  char *message = (char *)malloc(len);
954
  bool enqueued = false;
955
  if (message) {
956
    memcpy_P(message, p, len);
957
    enqueued = binary(message, len);
958
    free(message);
959
  }
960
  return enqueued;
961
}
962
#endif
963
 
964
IPAddress AsyncWebSocketClient::remoteIP() const {
965
  // Reads _client, then dereference it without any lock.
966
  // A concurrent _onDisconnect could null + delete the client between the check and the use.
967
  // Local capture ensures the pointer is read exactly once, eliminating the null-dereference.
968
  // (TOCTOU)
969
  AsyncClient *c = _client;
970
  if (!c) {
971
    return IPAddress((uint32_t)0U);
972
  }
973
  return c->remoteIP();
974
}
975
 
976
uint16_t AsyncWebSocketClient::remotePort() const {
977
  // Reads _client, then dereference it without any lock.
978
  // A concurrent _onDisconnect could null + delete the client between the check and the use.
979
  // Local capture ensures the pointer is read exactly once, eliminating the null-dereference.
980
  // (TOCTOU)
981
  AsyncClient *c = _client;
982
  if (!c) {
983
    return 0;
984
  }
985
  return c->remotePort();
986
}
987
 
988
/*
989
 * Async Web Socket - Each separate socket location
990
 */
991
 
992
void AsyncWebSocket::_handleEvent(AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
993
  if (_eventHandler != NULL) {
994
    _eventHandler(this, client, type, arg, data, len);
995
  }
996
}
997
 
998
AsyncWebSocketClient *AsyncWebSocket::_newClient(AsyncWebServerRequest *request) {
999
  asyncsrv::lock_guard_type lock(_ws_clients_lock);
1000
  _clients.emplace_back(request, this);
1001
  // we've just detached AsyncTCP client from AsyncWebServerRequest
1002
  _handleEvent(&_clients.back(), WS_EVT_CONNECT, request, NULL, 0);
1003
  // after user code completed CONNECT event callback we can delete req/response objects
1004
  delete request;
1005
  return &_clients.back();
1006
}
1007
 
1008
void AsyncWebSocket::_handleDisconnect(AsyncWebSocketClient *client) {
1009
  asyncsrv::lock_guard_type lock(_ws_clients_lock);
1010
  const auto client_id = client->id();
1011
  const auto iter = std::find_if(std::begin(_clients), std::end(_clients), [client_id](const AsyncWebSocketClient &c) {
1012
    return c.id() == client_id;
1013
  });
1014
  if (iter != std::end(_clients)) {
1015
    _clients.erase(iter);
1016
  }
1017
}
1018
 
1019
bool AsyncWebSocket::availableForWriteAll() {
1020
  asyncsrv::lock_guard_type lock(_ws_clients_lock);
1021
  return std::none_of(std::begin(_clients), std::end(_clients), [](const AsyncWebSocketClient &c) {
1022
    return c.queueIsFull();
1023
  });
1024
}
1025
 
1026
bool AsyncWebSocket::availableForWrite(uint32_t id) {
1027
  asyncsrv::lock_guard_type lock(_ws_clients_lock);
1028
  const auto iter = std::find_if(std::begin(_clients), std::end(_clients), [id](const AsyncWebSocketClient &c) {
1029
    return c.id() == id;
1030
  });
1031
  if (iter == std::end(_clients)) {
1032
    return true;
1033
  }
1034
  return !iter->queueIsFull();
1035
}
1036
 
1037
size_t AsyncWebSocket::count() const {
1038
  asyncsrv::lock_guard_type lock(_ws_clients_lock);
1039
  return std::count_if(std::begin(_clients), std::end(_clients), [](const AsyncWebSocketClient &c) {
1040
    return c.status() == WS_CONNECTED;
1041
  });
1042
}
1043
 
1044
AsyncWebSocketClient *AsyncWebSocket::client(uint32_t id) {
1045
  asyncsrv::lock_guard_type lock(_ws_clients_lock);
1046
  const auto iter = std::find_if(_clients.begin(), _clients.end(), [id](const AsyncWebSocketClient &c) {
1047
    return c.id() == id && c.status() == WS_CONNECTED;
1048
  });
1049
  if (iter == std::end(_clients)) {
1050
    return nullptr;
1051
  }
1052
 
1053
  return &(*iter);
1054
}
1055
 
1056
void AsyncWebSocket::close(uint32_t id, uint16_t code, const char *message) {
1057
  asyncsrv::lock_guard_type lock(_ws_clients_lock);
1058
  if (AsyncWebSocketClient *c = client(id)) {
1059
    c->close(code, message);
1060
  }
1061
}
1062
 
1063
void AsyncWebSocket::closeAll(uint16_t code, const char *message) {
1064
  asyncsrv::lock_guard_type lock(_ws_clients_lock);
1065
  for (auto &c : _clients) {
1066
    if (c.status() == WS_CONNECTED) {
1067
      c.close(code, message);
1068
    }
1069
  }
1070
}
1071
 
1072
void AsyncWebSocket::cleanupClients(uint16_t maxClients) {
1073
  asyncsrv::lock_guard_type lock(_ws_clients_lock);
1074
  const size_t c = count();
1075
  if (c > maxClients) {
1076
    async_ws_log_v("[%s] CLEANUP %" PRIu32 " (%u/%" PRIu16 ")", _url.c_str(), _clients.front().id(), c, maxClients);
1077
    _clients.front().close();
1078
  }
1079
 
1080
  for (auto i = _clients.begin(); i != _clients.end(); ++i) {
1081
    if (i->shouldBeDeleted()) {
1082
      _clients.erase(i);
1083
      break;
1084
    }
1085
  }
1086
}
1087
 
1088
bool AsyncWebSocket::ping(uint32_t id, const uint8_t *data, size_t len) {
1089
  asyncsrv::lock_guard_type lock(_ws_clients_lock);
1090
  AsyncWebSocketClient *c = client(id);
1091
  return c && c->ping(data, len);
1092
}
1093
 
1094
AsyncWebSocket::SendStatus AsyncWebSocket::pingAll(const uint8_t *data, size_t len) {
1095
  asyncsrv::lock_guard_type lock(_ws_clients_lock);
1096
  size_t hit = 0;
1097
  size_t miss = 0;
1098
  for (auto &c : _clients) {
1099
    if (c.status() == WS_CONNECTED && c.ping(data, len)) {
1100
      hit++;
1101
    } else {
1102
      miss++;
1103
    }
1104
  }
1105
  return hit == 0 ? DISCARDED : (miss == 0 ? ENQUEUED : PARTIALLY_ENQUEUED);
1106
}
1107
 
1108
bool AsyncWebSocket::text(uint32_t id, const uint8_t *message, size_t len) {
1109
  asyncsrv::lock_guard_type lock(_ws_clients_lock);
1110
  AsyncWebSocketClient *c = client(id);
1111
  return c && c->text(makeSharedBuffer(message, len));
1112
}
1113
bool AsyncWebSocket::text(uint32_t id, const char *message, size_t len) {
1114
  return text(id, (const uint8_t *)message, len);
1115
}
1116
bool AsyncWebSocket::text(uint32_t id, const char *message) {
1117
  return text(id, message, strlen(message));
1118
}
1119
bool AsyncWebSocket::text(uint32_t id, const String &message) {
1120
  return text(id, message.c_str(), message.length());
1121
}
1122
 
1123
#ifdef ESP8266
1124
bool AsyncWebSocket::text(uint32_t id, const __FlashStringHelper *data) {
1125
  PGM_P p = reinterpret_cast<PGM_P>(data);
1126
 
1127
  size_t n = 0;
1128
  while (true) {
1129
    if (pgm_read_byte(p + n) == 0) {
1130
      break;
1131
    }
1132
    n += 1;
1133
  }
1134
 
1135
  char *message = (char *)malloc(n + 1);
1136
  bool enqueued = false;
1137
  if (message) {
1138
    memcpy_P(message, p, n);
1139
    message[n] = 0;
1140
    enqueued = text(id, message, n);
1141
    free(message);
1142
  }
1143
  return enqueued;
1144
}
1145
#endif  // ESP8266
1146
 
1147
bool AsyncWebSocket::text(uint32_t id, AsyncWebSocketMessageBuffer *buffer) {
1148
  bool enqueued = false;
1149
  if (buffer) {
1150
    enqueued = text(id, std::move(buffer->_buffer));
1151
    delete buffer;
1152
  }
1153
  return enqueued;
1154
}
1155
bool AsyncWebSocket::text(uint32_t id, AsyncWebSocketSharedBuffer buffer) {
1156
  asyncsrv::lock_guard_type lock(_ws_clients_lock);
1157
  AsyncWebSocketClient *c = client(id);
1158
  return c && c->text(buffer);
1159
}
1160
 
1161
AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const uint8_t *message, size_t len) {
1162
  return textAll(makeSharedBuffer(message, len));
1163
}
1164
AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const char *message, size_t len) {
1165
  return textAll((const uint8_t *)message, len);
1166
}
1167
AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const char *message) {
1168
  return textAll(message, strlen(message));
1169
}
1170
AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const String &message) {
1171
  return textAll(message.c_str(), message.length());
1172
}
1173
#ifdef ESP8266
1174
AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const __FlashStringHelper *data) {
1175
  PGM_P p = reinterpret_cast<PGM_P>(data);
1176
 
1177
  size_t n = 0;
1178
  while (1) {
1179
    if (pgm_read_byte(p + n) == 0) {
1180
      break;
1181
    }
1182
    n += 1;
1183
  }
1184
 
1185
  char *message = (char *)malloc(n + 1);
1186
  AsyncWebSocket::SendStatus status = DISCARDED;
1187
  if (message) {
1188
    memcpy_P(message, p, n);
1189
    message[n] = 0;
1190
    status = textAll(message, n);
1191
    free(message);
1192
  }
1193
  return status;
1194
}
1195
#endif  // ESP8266
1196
AsyncWebSocket::SendStatus AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer *buffer) {
1197
  AsyncWebSocket::SendStatus status = DISCARDED;
1198
  if (buffer) {
1199
    status = textAll(std::move(buffer->_buffer));
1200
    delete buffer;
1201
  }
1202
  return status;
1203
}
1204
 
1205
AsyncWebSocket::SendStatus AsyncWebSocket::textAll(AsyncWebSocketSharedBuffer buffer) {
1206
  asyncsrv::lock_guard_type lock(_ws_clients_lock);
1207
  size_t hit = 0;
1208
  size_t miss = 0;
1209
  for (auto &c : _clients) {
1210
    if (c.status() == WS_CONNECTED && c.text(buffer)) {
1211
      hit++;
1212
    } else {
1213
      miss++;
1214
    }
1215
  }
1216
  return hit == 0 ? DISCARDED : (miss == 0 ? ENQUEUED : PARTIALLY_ENQUEUED);
1217
}
1218
 
1219
bool AsyncWebSocket::binary(uint32_t id, const uint8_t *message, size_t len) {
1220
  asyncsrv::lock_guard_type lock(_ws_clients_lock);
1221
  AsyncWebSocketClient *c = client(id);
1222
  return c && c->binary(makeSharedBuffer(message, len));
1223
}
1224
bool AsyncWebSocket::binary(uint32_t id, const char *message, size_t len) {
1225
  return binary(id, (const uint8_t *)message, len);
1226
}
1227
bool AsyncWebSocket::binary(uint32_t id, const char *message) {
1228
  return binary(id, message, strlen(message));
1229
}
1230
bool AsyncWebSocket::binary(uint32_t id, const String &message) {
1231
  return binary(id, message.c_str(), message.length());
1232
}
1233
 
1234
#ifdef ESP8266
1235
bool AsyncWebSocket::binary(uint32_t id, const __FlashStringHelper *data, size_t len) {
1236
  PGM_P p = reinterpret_cast<PGM_P>(data);
1237
  char *message = (char *)malloc(len);
1238
  bool enqueued = false;
1239
  if (message) {
1240
    memcpy_P(message, p, len);
1241
    enqueued = binary(id, message, len);
1242
    free(message);
1243
  }
1244
  return enqueued;
1245
}
1246
#endif  // ESP8266
1247
 
1248
bool AsyncWebSocket::binary(uint32_t id, AsyncWebSocketMessageBuffer *buffer) {
1249
  bool enqueued = false;
1250
  if (buffer) {
1251
    enqueued = binary(id, std::move(buffer->_buffer));
1252
    delete buffer;
1253
  }
1254
  return enqueued;
1255
}
1256
bool AsyncWebSocket::binary(uint32_t id, AsyncWebSocketSharedBuffer buffer) {
1257
  asyncsrv::lock_guard_type lock(_ws_clients_lock);
1258
  AsyncWebSocketClient *c = client(id);
1259
  return c && c->binary(buffer);
1260
}
1261
 
1262
AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const uint8_t *message, size_t len) {
1263
  return binaryAll(makeSharedBuffer(message, len));
1264
}
1265
AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const char *message, size_t len) {
1266
  return binaryAll((const uint8_t *)message, len);
1267
}
1268
AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const char *message) {
1269
  return binaryAll(message, strlen(message));
1270
}
1271
AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const String &message) {
1272
  return binaryAll(message.c_str(), message.length());
1273
}
1274
 
1275
#ifdef ESP8266
1276
AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const __FlashStringHelper *data, size_t len) {
1277
  PGM_P p = reinterpret_cast<PGM_P>(data);
1278
  char *message = (char *)malloc(len);
1279
  AsyncWebSocket::SendStatus status = DISCARDED;
1280
  if (message) {
1281
    memcpy_P(message, p, len);
1282
    status = binaryAll(message, len);
1283
    free(message);
1284
  }
1285
  return status;
1286
}
1287
#endif  // ESP8266
1288
 
1289
AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer *buffer) {
1290
  AsyncWebSocket::SendStatus status = DISCARDED;
1291
  if (buffer) {
1292
    status = binaryAll(std::move(buffer->_buffer));
1293
    delete buffer;
1294
  }
1295
  return status;
1296
}
1297
AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(AsyncWebSocketSharedBuffer buffer) {
1298
  asyncsrv::lock_guard_type lock(_ws_clients_lock);
1299
  size_t hit = 0;
1300
  size_t miss = 0;
1301
  for (auto &c : _clients) {
1302
    if (c.status() == WS_CONNECTED && c.binary(buffer)) {
1303
      hit++;
1304
    } else {
1305
      miss++;
1306
    }
1307
  }
1308
  return hit == 0 ? DISCARDED : (miss == 0 ? ENQUEUED : PARTIALLY_ENQUEUED);
1309
}
1310
 
1311
size_t AsyncWebSocket::printf(uint32_t id, const char *format, ...) {
1312
  AsyncWebSocketClient *c = client(id);
1313
  if (c) {
1314
    va_list arg;
1315
    va_start(arg, format);
1316
    size_t len = c->printf(format, arg);
1317
    va_end(arg);
1318
    return len;
1319
  }
1320
  return 0;
1321
}
1322
 
1323
size_t AsyncWebSocket::printfAll(const char *format, ...) {
1324
  va_list arg;
1325
  va_start(arg, format);
1326
  size_t len = vsnprintf(nullptr, 0, format, arg);
1327
  va_end(arg);
1328
 
1329
  if (len == 0) {
1330
    return 0;
1331
  }
1332
 
1333
  char *buffer = new char[len + 1];
1334
 
1335
  if (!buffer) {
1336
    return 0;
1337
  }
1338
 
1339
  va_start(arg, format);
1340
  len = vsnprintf(buffer, len + 1, format, arg);
1341
  va_end(arg);
1342
 
1343
  AsyncWebSocket::SendStatus status = textAll(buffer, len);
1344
  delete[] buffer;
1345
  return status == DISCARDED ? 0 : len;
1346
}
1347
 
1348
#ifdef ESP8266
1349
size_t AsyncWebSocket::printf_P(uint32_t id, PGM_P formatP, ...) {
1350
  AsyncWebSocketClient *c = client(id);
1351
  if (c != NULL) {
1352
    va_list arg;
1353
    va_start(arg, formatP);
1354
    size_t len = c->printf_P(formatP, arg);
1355
    va_end(arg);
1356
    return len;
1357
  }
1358
  return 0;
1359
}
1360
 
1361
size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) {
1362
  va_list arg;
1363
  va_start(arg, formatP);
1364
  size_t len = vsnprintf_P(nullptr, 0, formatP, arg);
1365
  va_end(arg);
1366
 
1367
  if (len == 0) {
1368
    return 0;
1369
  }
1370
 
1371
  char *buffer = new char[len + 1];
1372
 
1373
  if (!buffer) {
1374
    return 0;
1375
  }
1376
 
1377
  va_start(arg, formatP);
1378
  len = vsnprintf_P(buffer, len + 1, formatP, arg);
1379
  va_end(arg);
1380
 
1381
  AsyncWebSocket::SendStatus status = textAll(buffer, len);
1382
  delete[] buffer;
1383
  return status == DISCARDED ? 0 : len;
1384
}
1385
#endif
1386
 
1387
const char __WS_STR_CONNECTION[] PROGMEM = {"Connection"};
1388
const char __WS_STR_UPGRADE[] PROGMEM = {"Upgrade"};
1389
const char __WS_STR_ORIGIN[] PROGMEM = {"Origin"};
1390
const char __WS_STR_COOKIE[] PROGMEM = {"Cookie"};
1391
const char __WS_STR_VERSION[] PROGMEM = {"Sec-WebSocket-Version"};
1392
const char __WS_STR_KEY[] PROGMEM = {"Sec-WebSocket-Key"};
1393
const char __WS_STR_PROTOCOL[] PROGMEM = {"Sec-WebSocket-Protocol"};
1394
const char __WS_STR_ACCEPT[] PROGMEM = {"Sec-WebSocket-Accept"};
1395
const char __WS_STR_UUID[] PROGMEM = {"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"};
1396
 
1397
#define WS_STR_UUID_LEN 36
1398
 
1399
#define WS_STR_CONNECTION FPSTR(__WS_STR_CONNECTION)
1400
#define WS_STR_UPGRADE    FPSTR(__WS_STR_UPGRADE)
1401
#define WS_STR_ORIGIN     FPSTR(__WS_STR_ORIGIN)
1402
#define WS_STR_COOKIE     FPSTR(__WS_STR_COOKIE)
1403
#define WS_STR_VERSION    FPSTR(__WS_STR_VERSION)
1404
#define WS_STR_KEY        FPSTR(__WS_STR_KEY)
1405
#define WS_STR_PROTOCOL   FPSTR(__WS_STR_PROTOCOL)
1406
#define WS_STR_ACCEPT     FPSTR(__WS_STR_ACCEPT)
1407
#define WS_STR_UUID       FPSTR(__WS_STR_UUID)
1408
 
1409
bool AsyncWebSocket::canHandle(AsyncWebServerRequest *request) const {
1410
  return _enabled && request->isWebSocketUpgrade() && request->url().equals(_url);
1411
}
1412
 
1413
void AsyncWebSocket::handleRequest(AsyncWebServerRequest *request) {
1414
  if (!request->hasHeader(WS_STR_VERSION) || !request->hasHeader(WS_STR_KEY)) {
1415
    request->send(400);
1416
    return;
1417
  }
1418
  if (_handshakeHandler != nullptr) {
1419
    if (!_handshakeHandler(request)) {
1420
      request->send(401);
1421
      return;
1422
    }
1423
  }
1424
  const AsyncWebHeader *version = request->getHeader(WS_STR_VERSION);
1425
  if (version->value().toInt() != 13) {
1426
    AsyncWebServerResponse *response = request->beginResponse(400);
1427
    response->addHeader(WS_STR_VERSION, T_13);
1428
    request->send(response);
1429
    return;
1430
  }
1431
  const AsyncWebHeader *key = request->getHeader(WS_STR_KEY);
1432
  AsyncWebServerResponse *response = new AsyncWebSocketResponse(key->value(), this);
1433
  if (response == NULL) {
1434
    async_ws_log_e("Failed to allocate");
1435
    request->abort();
1436
    return;
1437
  }
1438
  if (request->hasHeader(WS_STR_PROTOCOL)) {
1439
    const AsyncWebHeader *protocol = request->getHeader(WS_STR_PROTOCOL);
1440
    // ToDo: check protocol
1441
    response->addHeader(WS_STR_PROTOCOL, protocol->value());
1442
  }
1443
  request->send(response);
1444
}
1445
 
1446
AsyncWebSocketMessageBuffer *AsyncWebSocket::makeBuffer(size_t size) {
1447
  return new AsyncWebSocketMessageBuffer(size);
1448
}
1449
 
1450
AsyncWebSocketMessageBuffer *AsyncWebSocket::makeBuffer(const uint8_t *data, size_t size) {
1451
  return new AsyncWebSocketMessageBuffer(data, size);
1452
}
1453
 
1454
/*
1455
 * Response to Web Socket request - sends the authorization and detaches the TCP Client from the web server
1456
 * Authentication code from https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSockets.cpp#L480
1457
 */
1458
 
1459
AsyncWebSocketResponse::AsyncWebSocketResponse(const String &key, AsyncWebSocket *server) : _server(server) {
1460
  _code = 101;
1461
  _sendContentLength = false;
1462
 
1463
  uint8_t hash[20];
1464
  char buffer[33];
1465
 
1466
#if defined(ESP8266) || defined(TARGET_RP2040) || defined(PICO_RP2040) || defined(PICO_RP2350) || defined(TARGET_RP2350)
1467
  sha1(key + WS_STR_UUID, hash);
1468
#else
1469
  String k;
1470
  if (!k.reserve(key.length() + WS_STR_UUID_LEN)) {
1471
    async_ws_log_e("Failed to allocate");
1472
    return;
1473
  }
1474
  k.concat(key);
1475
  k.concat(WS_STR_UUID);
1476
#ifdef LIBRETINY
1477
  mbedtls_sha1_context ctx;
1478
  mbedtls_sha1_init(&ctx);
1479
  mbedtls_sha1_starts(&ctx);
1480
  mbedtls_sha1_update(&ctx, (const uint8_t *)k.c_str(), k.length());
1481
  mbedtls_sha1_finish(&ctx, hash);
1482
  mbedtls_sha1_free(&ctx);
1483
#else
1484
  SHA1Builder sha1;
1485
  sha1.begin();
1486
  sha1.add((const uint8_t *)k.c_str(), k.length());
1487
  sha1.calculate();
1488
  sha1.getBytes(hash);
1489
#endif
1490
#endif
1491
  base64_encodestate _state;
1492
  base64_init_encodestate(&_state);
1493
  int len = base64_encode_block((const char *)hash, 20, buffer, &_state);
1494
  len = base64_encode_blockend((buffer + len), &_state);
1495
  addHeader(WS_STR_CONNECTION, WS_STR_UPGRADE);
1496
  addHeader(WS_STR_UPGRADE, T_WS);
1497
  addHeader(WS_STR_ACCEPT, buffer);
1498
}
1499
 
1500
void AsyncWebSocketResponse::_respond(AsyncWebServerRequest *request) {
1501
  if (_state == RESPONSE_FAILED) {
1502
    request->client()->close();
1503
    return;
1504
  }
1505
  // unbind client's onAck callback from AsyncWebServerRequest's, we will destroy it on next callback and steal the client,
1506
  // can't do it now 'cause now we are in AsyncWebServerRequest::_onAck 's stack actually
1507
  // here we are loosing time on one RTT delay, but with current design we can't get rid of Req/Resp objects other way
1508
  _request = request;
1509
  request->client()->onAck(
1510
    [](void *r, AsyncClient *c, size_t len, uint32_t time) {
1511
      if (len) {
1512
        static_cast<AsyncWebSocketResponse *>(r)->_switchClient();
1513
      }
1514
    },
1515
    this
1516
  );
1517
  String out;
1518
  _assembleHead(out, request->version());
1519
  request->client()->write(out.c_str(), _headLength);
1520
  _state = RESPONSE_WAIT_ACK;
1521
}
1522
 
1523
void AsyncWebSocketResponse::_switchClient() {
1524
  // detach client from request
1525
  _server->_newClient(_request);
1526
  // _newClient() would also destruct _request and *this
1527
}