Skip to content

Commit 6cb89cb

Browse files
Merge pull request #353 from DeqingSun/patch-1
Deal with safari fragmented pong data
2 parents d7bbff6 + 3c9bb75 commit 6cb89cb

File tree

1 file changed

+45
-10
lines changed

1 file changed

+45
-10
lines changed

src/AsyncWebSocket.cpp

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -515,12 +515,15 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) {
515515
_pinfo.index = 0;
516516
_pinfo.final = (fdata[0] & 0x80) != 0;
517517
_pinfo.opcode = fdata[0] & 0x0F;
518-
_pinfo.masked = (fdata[1] & 0x80) != 0;
518+
_pinfo.masked = ((fdata[1] & 0x80) != 0) ? 1 : 0;
519519
_pinfo.len = fdata[1] & 0x7F;
520520

521-
// async_ws_log_d("WS[%" PRIu32 "]: _onData: %" PRIu32, _clientId, plen);
522-
// async_ws_log_d("WS[%" PRIu32 "]: _status = %" PRIu32, _clientId, _status);
523-
// async_ws_log_d("WS[%" PRIu32 "]: _pinfo: index: %" PRIu64 ", final: %" PRIu8 ", opcode: %" PRIu8 ", masked: %" PRIu8 ", len: %" PRIu64, _clientId, _pinfo.index, _pinfo.final, _pinfo.opcode, _pinfo.masked, _pinfo.len);
521+
// async_ws_log_w("WS[%" PRIu32 "]: _onData: %" PRIu32, _clientId, plen);
522+
// async_ws_log_w("WS[%" PRIu32 "]: _status = %" PRIu32, _clientId, _status);
523+
// async_ws_log_w(
524+
// "WS[%" PRIu32 "]: _pinfo: index: %" PRIu64 ", final: %" PRIu8 ", opcode: %" PRIu8 ", masked: %" PRIu8 ", len: %" PRIu64, _clientId, _pinfo.index,
525+
// _pinfo.final, _pinfo.opcode, _pinfo.masked, _pinfo.len
526+
// );
524527

525528
data += 2;
526529
plen -= 2;
@@ -536,18 +539,50 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) {
536539
data += 8;
537540
plen -= 8;
538541
}
542+
}
543+
544+
if (_pinfo.masked) {
545+
// Read mask bytes (may be fragmented across packets in Safari)
546+
size_t mask_offset = 0;
547+
548+
// If we're resuming from a previous fragmented read, check _pinfo.index
549+
if (_pstate == 1 && _pinfo.index < 4) {
550+
mask_offset = _pinfo.index;
551+
}
539552

540-
if (_pinfo.masked
541-
&& plen >= 4) { // if ws.close() is called, Safari sends a close frame with plen 2 and masked bit set. We must not decrement plen which is already 0.
542-
memcpy(_pinfo.mask, data, 4);
543-
data += 4;
544-
plen -= 4;
553+
// Read as many mask bytes as available
554+
while (mask_offset < 4 && plen > 0) {
555+
_pinfo.mask[mask_offset++] = *data++;
556+
plen--;
557+
}
558+
559+
// Check if we have all 4 mask bytes
560+
if (mask_offset < 4) {
561+
// Incomplete mask
562+
if (_pinfo.opcode == WS_DISCONNECT && plen == 0) {
563+
// Safari close frame edge case: masked bit set but no mask data
564+
// async_ws_log_w("WS[%" PRIu32 "]: close frame with incomplete mask, treating as unmasked", _clientId);
565+
_pinfo.masked = 0;
566+
_pinfo.index = 0;
567+
} else {
568+
// Wait for more data
569+
// async_ws_log_w("WS[%" PRIu32 "]: waiting for more mask data: read=%zu/4", _clientId, mask_offset);
570+
_pinfo.index = mask_offset; // Save progress
571+
_pstate = 1;
572+
return;
573+
}
574+
} else {
575+
// All mask bytes received
576+
// async_ws_log_w("WS[%" PRIu32 "]: mask complete", _clientId);
577+
_pinfo.index = 0; // Reset index for payload processing
545578
}
546579
}
547580

548581
const size_t datalen = std::min((size_t)(_pinfo.len - _pinfo.index), plen);
549582
const auto datalast = data[datalen];
550583

584+
// async_ws_log_w("WS[%" PRIu32 "]: _processing data: datalen=%" PRIu32 ", plen=%" PRIu32, _clientId, datalen, plen);
585+
551586
if (_pinfo.masked) {
552587
for (size_t i = 0; i < datalen; i++) {
553588
data[i] ^= _pinfo.mask[(_pinfo.index + i) % 4];
@@ -606,7 +641,7 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) {
606641
}
607642
}
608643
} else {
609-
// os_printf("frame error: len: %u, index: %llu, total: %llu\n", datalen, _pinfo.index, _pinfo.len);
644+
// async_ws_log_w("frame error: len: %u, index: %llu, total: %llu\n", datalen, _pinfo.index, _pinfo.len);
610645
// what should we do?
611646
break;
612647
}

0 commit comments

Comments
 (0)