SNode.C
Loading...
Searching...
No Matches
Receiver.cpp
Go to the documentation of this file.
1/*
2 * SNode.C - A Slim Toolkit for Network Communication
3 * Copyright (C) Volker Christian <me@vchrist.at>
4 * 2020, 2021, 2022, 2023, 2024, 2025
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/*
21 * MIT License
22 *
23 * Permission is hereby granted, free of charge, to any person obtaining a copy
24 * of this software and associated documentation files (the "Software"), to deal
25 * in the Software without restriction, including without limitation the rights
26 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27 * copies of the Software, and to permit persons to whom the Software is
28 * furnished to do so, subject to the following conditions:
29 *
30 * The above copyright notice and this permission notice shall be included in
31 * all copies or substantial portions of the Software.
32 *
33 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39 * THE SOFTWARE.
40 */
41
42#include "web/websocket/Receiver.h"
43
44#include "core/socket/stream/SocketConnection.h"
45
46#ifndef DOXYGEN_SHOULD_SKIP_THIS
47
48#include "log/Logger.h"
49#include "utils/hexdump.h"
50
51#include <endian.h>
52#include <string>
53
54#endif /* DOXYGEN_SHOULD_SKIP_THIS */
55
56namespace web::websocket {
57
58 Receiver::Receiver(core::socket::stream::SocketConnection* socketConnection)
59 : socketConnection(socketConnection) {
60 }
61
63 }
64
65 std::size_t Receiver::receive() {
66 std::size_t ret = 0;
67 std::size_t consumed = 0;
68
69 // dumpFrame(chunk, chunkLen);
70
71 do {
72 switch (parserState) {
74 parserState = ParserState::OPCODE;
75 [[fallthrough]];
77 ret = readOpcode();
78 break;
80 ret = readLength();
81 break;
83 ret = readELength();
84 break;
86 ret = readMaskingKey();
87 break;
89 ret = readPayload();
90 break;
93 reset();
94 break;
95 }
96 consumed += ret;
97 } while (ret > 0 && parserState != ParserState::BEGIN && parserState != ParserState::ERROR);
98
99 return consumed;
100 }
101
102 std::size_t Receiver::readOpcode() {
103 char byte = 0;
104 const std::size_t ret = readFrameData(&byte, 1);
105
106 if (ret > 0) {
107 const uint8_t opCodeByte = static_cast<uint8_t>(byte);
108
109 fin = (opCodeByte & 0b10000000) != 0;
110 opCode = opCodeByte & 0b00001111;
111
112 if (!continuation) {
114 parserState = ParserState::LENGTH;
115 } else if (opCode == 0) {
116 parserState = ParserState::LENGTH;
117 } else {
118 parserState = ParserState::ERROR;
119 errorState = 1002;
120 LOG(ERROR) << "WebSocket: Error opcode in continuation frame";
121 }
122 continuation = !fin;
123 }
124
125 return ret;
126 }
127
128 std::size_t Receiver::readLength() {
129 char byte = 0;
130 const std::size_t ret = readFrameData(&byte, 1);
131
132 if (ret > 0) {
133 const uint8_t lengthByte = static_cast<uint8_t>(byte);
134
135 masked = (lengthByte & 0b10000000) != 0;
136 payLoadNumBytes = payLoadNumBytesLeft = lengthByte & 0b01111111;
137
138 if (payLoadNumBytes > 125) {
139 switch (payLoadNumBytes) {
140 case 126:
142 break;
143 case 127:
145 break;
146 }
147 parserState = ParserState::ELENGTH;
149 } else {
150 if (masked) {
151 parserState = ParserState::MASKINGKEY;
152 } else if (payLoadNumBytes > 0) {
153 parserState = ParserState::PAYLOAD;
154 } else {
155 if (fin) {
157 }
158 reset();
159 }
160 }
161 }
162
163 return ret;
164 }
165
166 std::size_t Receiver::readFrameData(char* chunk, std::size_t chunkLen) {
167 return socketConnection->readFromPeer(chunk, chunkLen);
168 }
169
170 std::size_t Receiver::readELength2() {
171 char elengthChunk[2];
172
173 const std::size_t ret = readFrameData(elengthChunk, elengthNumBytesLeft);
174
175 const std::size_t cursor = static_cast<std::size_t>(elengthNumBytes - elengthNumBytesLeft);
176 for (std::size_t i = 0; i < ret; i++) {
177 eLength2.asBytes[cursor + i] = elengthChunk[i];
179 }
180
181 if (elengthNumBytesLeft == 0) {
182 payLoadNumBytes = payLoadNumBytesLeft = be16toh(eLength2.asValue);
183 }
184
185 return ret;
186 }
187
188 std::size_t Receiver::readELength8() {
189 char elengthChunk[8];
190
191 const std::size_t ret = readFrameData(elengthChunk, elengthNumBytesLeft);
192
193 const std::size_t cursor = static_cast<std::size_t>(elengthNumBytes - elengthNumBytesLeft);
194 for (std::size_t i = 0; i < ret; i++) {
195 eLength8.asBytes[cursor + i] = elengthChunk[i];
197 }
198
199 if (elengthNumBytesLeft == 0) {
200 payLoadNumBytes = payLoadNumBytesLeft = be64toh(eLength8.asValue);
201 }
202
203 return ret;
204 }
205
206 std::size_t Receiver::readELength() {
207 std::size_t ret = 0;
208
209 switch (elengthNumBytes) {
210 case 2:
211 ret = readELength2();
212 break;
213 case 8:
214 ret = readELength8();
215 break;
216 }
217
218 if (elengthNumBytesLeft == 0) {
219 if ((payLoadNumBytes & static_cast<uint64_t>(0x01) << 63) != 0) {
220 parserState = ParserState::ERROR;
221 errorState = 1004;
222 } else if (masked) {
223 parserState = ParserState::MASKINGKEY;
224 } else {
225 parserState = ParserState::PAYLOAD;
226 }
227 }
228
229 return ret;
230 }
231
232 std::size_t Receiver::readMaskingKey() {
233 char maskingKeyChunk[4]{};
234
235 const std::size_t ret = readFrameData(maskingKeyChunk, maskingKeyNumBytesLeft);
236
237 const std::size_t cursor = static_cast<std::size_t>(maskingKeyNumBytes - maskingKeyNumBytesLeft);
238 for (std::size_t i = 0; i < ret; i++) {
239 maskingKey[cursor + i] = maskingKeyChunk[i];
241 }
242
243 if (maskingKeyNumBytesLeft == 0) {
244 if (payLoadNumBytes > 0) {
245 parserState = ParserState::PAYLOAD;
246 } else {
247 if (fin) {
249 }
250 reset();
251 }
252 }
253
254 return ret;
255 }
256
257 std::size_t Receiver::readPayload() {
258 char payloadChunk[MAX_PAYLOAD_JUNK_LEN]{};
259
260 const std::size_t payloadChunkLeft = (MAX_PAYLOAD_JUNK_LEN <= payLoadNumBytesLeft) ? static_cast<std::size_t>(MAX_PAYLOAD_JUNK_LEN)
261 : static_cast<std::size_t>(payLoadNumBytesLeft);
262 const std::size_t ret = readFrameData(payloadChunk, payloadChunkLeft);
263
264 if (ret > 0) {
265 const std::size_t payloadChunkLen = static_cast<std::size_t>(ret);
266
267 if (masked) {
268 for (std::size_t i = 0; i < payloadChunkLen; i++) {
269 *(payloadChunk + i) = *(payloadChunk + i) ^ *(maskingKey + (i + payLoadNumBytes - payLoadNumBytesLeft) % 4);
270 }
271 }
272
273 LOG(TRACE) << "WebSocket receive: Frame data\n" << utils::hexDump(payloadChunk, payloadChunkLen, 32, true);
274
275 onMessageData(payloadChunk, payloadChunkLen);
276
277 payLoadNumBytesLeft -= payloadChunkLen;
278 }
279
280 if (payLoadNumBytesLeft == 0) {
281 if (fin) {
283 }
284 reset();
285 }
286
287 return ret;
288 }
289
290 void Receiver::reset() {
291 parserState = ParserState::BEGIN;
292
293 fin = false;
294 continuation = false;
295 masked = false;
296
297 opCode = 0;
298
299 elengthNumBytes = 0;
301
302 payLoadNumBytes = 0;
304
307
308 errorState = 0;
309 }
310
311} // namespace web::websocket
#define MAX_PAYLOAD_JUNK_LEN
Definition Receiver.h:59
virtual std::size_t readFromPeer(char *chunk, std::size_t chunkLen)=0
std::size_t readELength2()
Definition Receiver.cpp:170
virtual void onMessageEnd()=0
std::size_t receive()
Definition Receiver.cpp:65
virtual void onMessageStart(int opCode)=0
std::size_t readLength()
Definition Receiver.cpp:128
std::size_t readMaskingKey()
Definition Receiver.cpp:232
std::size_t readELength()
Definition Receiver.cpp:206
std::size_t readFrameData(char *chunk, std::size_t chunkLen)
Definition Receiver.cpp:166
virtual void onMessageData(const char *chunk, uint64_t chunkLen)=0
Receiver(core::socket::stream::SocketConnection *socketConnection)
Definition Receiver.cpp:58
virtual void onMessageError(uint16_t errnum)=0
core::socket::stream::SocketConnection * socketConnection
Definition Receiver.h:126
std::size_t readPayload()
Definition Receiver.cpp:257
std::size_t readOpcode()
Definition Receiver.cpp:102
std::size_t readELength8()
Definition Receiver.cpp:188
std::string hexDump(const char *bytes, uint64_t length, int prefixLength, bool prefixAtFirstLine)
Definition hexdump.cpp:66