Skip to content
This repository was archived by the owner on Mar 5, 2026. It is now read-only.

Commit b6b43d3

Browse files
adapt0me-no-dev
authored andcommitted
Handle multiple WebSocket frames within a TCP packet (me-no-dev#338)
1 parent 6986587 commit b6b43d3

2 files changed

Lines changed: 78 additions & 68 deletions

File tree

src/AsyncWebSocket.cpp

Lines changed: 77 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -565,83 +565,93 @@ void AsyncWebSocketClient::_onDisconnect(){
565565
_server->_handleDisconnect(this);
566566
}
567567

568-
void AsyncWebSocketClient::_onData(void *buf, size_t plen){
568+
void AsyncWebSocketClient::_onData(void *pbuf, size_t plen){
569569
_lastMessageTime = millis();
570-
uint8_t *fdata = (uint8_t*)buf;
571-
uint8_t * data = fdata;
572-
if(!_pstate){
573-
_pinfo.index = 0;
574-
_pinfo.final = (fdata[0] & 0x80) != 0;
575-
_pinfo.opcode = fdata[0] & 0x0F;
576-
_pinfo.masked = (fdata[1] & 0x80) != 0;
577-
_pinfo.len = fdata[1] & 0x7F;
578-
data += 2;
579-
plen = plen - 2;
580-
if(_pinfo.len == 126){
581-
_pinfo.len = fdata[3] | (uint16_t)(fdata[2]) << 8;
570+
uint8_t *data = (uint8_t*)pbuf;
571+
while(plen > 0){
572+
if(!_pstate){
573+
const uint8_t *fdata = data;
574+
_pinfo.index = 0;
575+
_pinfo.final = (fdata[0] & 0x80) != 0;
576+
_pinfo.opcode = fdata[0] & 0x0F;
577+
_pinfo.masked = (fdata[1] & 0x80) != 0;
578+
_pinfo.len = fdata[1] & 0x7F;
582579
data += 2;
583-
plen = plen - 2;
584-
} else if(_pinfo.len == 127){
585-
_pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | (uint64_t)(fdata[5]) << 32 | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56;
586-
data += 8;
587-
plen = plen - 8;
588-
}
580+
plen -= 2;
581+
if(_pinfo.len == 126){
582+
_pinfo.len = fdata[3] | (uint16_t)(fdata[2]) << 8;
583+
data += 2;
584+
plen -= 2;
585+
} else if(_pinfo.len == 127){
586+
_pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | (uint64_t)(fdata[5]) << 32 | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56;
587+
data += 8;
588+
plen -= 8;
589+
}
589590

590-
if(_pinfo.masked){
591-
memcpy(_pinfo.mask, data, 4);
592-
data += 4;
593-
plen = plen - 4;
594-
size_t i;
595-
for(i=0;i<plen;i++)
596-
data[i] = data[i] ^ _pinfo.mask[(_pinfo.index+i)%4];
591+
if(_pinfo.masked){
592+
memcpy(_pinfo.mask, data, 4);
593+
data += 4;
594+
plen -= 4;
595+
}
597596
}
598-
} else {
597+
598+
const size_t datalen = std::min((size_t)(_pinfo.len - _pinfo.index), plen);
599+
const auto datalast = data[datalen];
600+
599601
if(_pinfo.masked){
600-
size_t i;
601-
for(i=0;i<plen;i++)
602-
data[i] = data[i] ^ _pinfo.mask[(_pinfo.index+i)%4];
602+
for(size_t i=0;i<datalen;i++)
603+
data[i] ^= _pinfo.mask[(_pinfo.index+i)%4];
603604
}
604-
}
605-
if((plen + _pinfo.index) < _pinfo.len){
606-
_pstate = 1;
607605

608-
if(_pinfo.index == 0){
609-
if(_pinfo.opcode){
610-
_pinfo.message_opcode = _pinfo.opcode;
611-
_pinfo.num = 0;
612-
} else _pinfo.num += 1;
613-
}
614-
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, plen);
615-
616-
_pinfo.index += plen;
617-
} else if((plen + _pinfo.index) == _pinfo.len){
618-
_pstate = 0;
619-
if(_pinfo.opcode == WS_DISCONNECT){
620-
if(plen){
621-
uint16_t reasonCode = (uint16_t)(data[0] << 8) + data[1];
622-
char * reasonString = (char*)(data+2);
623-
if(reasonCode > 1001){
624-
_server->_handleEvent(this, WS_EVT_ERROR, (void *)&reasonCode, (uint8_t*)reasonString, strlen(reasonString));
625-
}
606+
if((datalen + _pinfo.index) < _pinfo.len){
607+
_pstate = 1;
608+
609+
if(_pinfo.index == 0){
610+
if(_pinfo.opcode){
611+
_pinfo.message_opcode = _pinfo.opcode;
612+
_pinfo.num = 0;
613+
} else _pinfo.num += 1;
626614
}
627-
if(_status == WS_DISCONNECTING){
628-
_status = WS_DISCONNECTED;
629-
_client->close(true);
630-
} else {
631-
_status = WS_DISCONNECTING;
632-
_queueControl(new AsyncWebSocketControl(WS_DISCONNECT, data, plen));
615+
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, datalen);
616+
617+
_pinfo.index += datalen;
618+
} else if((datalen + _pinfo.index) == _pinfo.len){
619+
_pstate = 0;
620+
if(_pinfo.opcode == WS_DISCONNECT){
621+
if(datalen){
622+
uint16_t reasonCode = (uint16_t)(data[0] << 8) + data[1];
623+
char * reasonString = (char*)(data+2);
624+
if(reasonCode > 1001){
625+
_server->_handleEvent(this, WS_EVT_ERROR, (void *)&reasonCode, (uint8_t*)reasonString, strlen(reasonString));
626+
}
627+
}
628+
if(_status == WS_DISCONNECTING){
629+
_status = WS_DISCONNECTED;
630+
_client->close(true);
631+
} else {
632+
_status = WS_DISCONNECTING;
633+
_queueControl(new AsyncWebSocketControl(WS_DISCONNECT, data, datalen));
634+
}
635+
} else if(_pinfo.opcode == WS_PING){
636+
_queueControl(new AsyncWebSocketControl(WS_PONG, data, datalen));
637+
} else if(_pinfo.opcode == WS_PONG){
638+
if(datalen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0)
639+
_server->_handleEvent(this, WS_EVT_PONG, NULL, data, datalen);
640+
} else if(_pinfo.opcode < 8){//continuation or text/binary frame
641+
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, datalen);
633642
}
634-
} else if(_pinfo.opcode == WS_PING){
635-
_queueControl(new AsyncWebSocketControl(WS_PONG, data, plen));
636-
} else if(_pinfo.opcode == WS_PONG){
637-
if(plen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0)
638-
_server->_handleEvent(this, WS_EVT_PONG, NULL, (uint8_t*)data, plen);
639-
} else if(_pinfo.opcode < 8){//continuation or text/binary frame
640-
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, plen);
643+
} else {
644+
//os_printf("frame error: len: %u, index: %llu, total: %llu\n", datalen, _pinfo.index, _pinfo.len);
645+
//what should we do?
646+
break;
641647
}
642-
} else {
643-
//os_printf("frame error: len: %u, index: %llu, total: %llu\n", plen, _pinfo.index, _pinfo.len);
644-
//what should we do?
648+
649+
// restore byte as _handleEvent may have added a null terminator i.e., data[len] = 0;
650+
if (datalen > 0)
651+
data[datalen] = datalast;
652+
653+
data += datalen;
654+
plen -= datalen;
645655
}
646656
}
647657

src/AsyncWebSocket.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ class AsyncWebSocketClient {
214214
void _onPoll();
215215
void _onTimeout(uint32_t time);
216216
void _onDisconnect();
217-
void _onData(void *buf, size_t plen);
217+
void _onData(void *pbuf, size_t plen);
218218
};
219219

220220
typedef std::function<void(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len)> AwsEventHandler;

0 commit comments

Comments
 (0)