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#ifndef DOXYGEN_SHOULD_SKIP_THIS
45
46#include "log/Logger.h"
47#include "utils/hexdump.h"
48
49#include <endian.h>
50#include <string>
51
52#endif /* DOXYGEN_SHOULD_SKIP_THIS */
53
54#ifndef MAX_PAYLOAD_CHUNK_LEN
55#define MAX_PAYLOAD_CHUNK_LEN 16384
56#endif
57
58namespace web::websocket {
59
60 Receiver::Receiver(bool maskingExpected)
61 : maskingExpected(maskingExpected) {
62 }
63
65 }
66
67 std::size_t Receiver::receive() {
68 std::size_t ret = 0;
69 std::size_t consumed = 0;
70
71 // dumpFrame(chunk, chunkLen);
72
73 do {
74 switch (parserState) {
76 parserState = ParserState::OPCODE;
77 [[fallthrough]];
79 ret = readOpcode();
80 break;
82 ret = readLength();
83 break;
85 ret = readELength();
86 break;
88 ret = readMaskingKey();
89 break;
91 ret = readPayload();
92 break;
95 reset();
96 break;
97 }
98 consumed += ret;
99 } while (ret > 0 && parserState != ParserState::BEGIN && parserState != ParserState::ERROR);
100
101 return consumed;
102 }
103
104 std::size_t Receiver::getPayloadTotalRead() const {
105 return payloadTotalRead;
106 }
107
108 std::size_t Receiver::readFrameData(char* chunk, std::size_t chunkLen) {
109 return readFrameChunk(chunk, chunkLen);
110 }
111
112 std::size_t Receiver::readOpcode() {
113 char byte = 0;
114 const std::size_t ret = readFrameData(&byte, 1);
115
116 if (ret > 0) {
117 const uint8_t opCodeByte = static_cast<uint8_t>(byte);
118
119 fin = (opCodeByte & 0b10000000) != 0;
120 opCode = opCodeByte & 0b00001111;
121
122 if (!continuation) {
124 parserState = ParserState::LENGTH;
125 } else if (opCode == 0) {
126 parserState = ParserState::LENGTH;
127 } else {
128 parserState = ParserState::ERROR;
129 errorState = 1002;
130 LOG(ERROR) << "WebSocket: Error opcode in continuation frame";
131 }
132 continuation = !fin;
133 }
134
135 return ret;
136 }
137
138 std::size_t Receiver::readLength() {
139 char byte = 0;
140 const std::size_t ret = readFrameData(&byte, 1);
141
142 if (ret > 0) {
143 const uint8_t lengthByte = static_cast<uint8_t>(byte);
144
145 masked = (lengthByte & 0b10000000) != 0;
146 if (masked == maskingExpected) {
147 payloadNumBytes = payloadNumBytesLeft = lengthByte & 0b01111111;
148
149 if (payloadNumBytes > 125) {
150 switch (payloadNumBytes) {
151 case 126:
153 break;
154 case 127:
156 break;
157 }
158 parserState = ParserState::ELENGTH;
160 } else {
161 if (masked) {
162 parserState = ParserState::MASKINGKEY;
163 } else if (payloadNumBytes > 0) {
164 parserState = ParserState::PAYLOAD;
165 } else {
166 if (fin) {
168 }
169 reset();
170 }
171 }
172 } else {
173 parserState = ParserState::ERROR;
174 }
175 }
176
177 return ret;
178 }
179
180 std::size_t Receiver::readELength2() {
181 char elengthChunk[2]{};
182
183 const std::size_t ret = readFrameData(elengthChunk, elengthNumBytesLeft);
184
185 const std::size_t cursor = static_cast<std::size_t>(elengthNumBytes - elengthNumBytesLeft);
186 for (std::size_t i = 0; i < ret; i++) {
187 eLength2.asBytes[cursor + i] = elengthChunk[i];
189 }
190
191 if (elengthNumBytesLeft == 0) {
192 payloadNumBytes = payloadNumBytesLeft = be16toh(eLength2.asValue);
193 }
194
195 return ret;
196 }
197
198 std::size_t Receiver::readELength8() {
199 char elengthChunk[8]{};
200
201 const std::size_t ret = readFrameData(elengthChunk, elengthNumBytesLeft);
202
203 const std::size_t cursor = static_cast<std::size_t>(elengthNumBytes - elengthNumBytesLeft);
204 for (std::size_t i = 0; i < ret; i++) {
205 eLength8.asBytes[cursor + i] = elengthChunk[i];
207 }
208
209 if (elengthNumBytesLeft == 0) {
210 payloadNumBytes = payloadNumBytesLeft = be64toh(eLength8.asValue);
211 }
212
213 return ret;
214 }
215
216 std::size_t Receiver::readELength() {
217 std::size_t ret = 0;
218
219 switch (elengthNumBytes) {
220 case 2:
221 ret = readELength2();
222 break;
223 case 8:
224 ret = readELength8();
225 break;
226 }
227
228 if (elengthNumBytesLeft == 0) {
229 if ((payloadNumBytes & static_cast<uint64_t>(0x01) << 63) != 0) {
230 parserState = ParserState::ERROR;
231 errorState = 1004;
232 } else if (masked) {
233 parserState = ParserState::MASKINGKEY;
234 } else {
235 parserState = ParserState::PAYLOAD;
236 }
237 }
238
239 return ret;
240 }
241
242 std::size_t Receiver::readMaskingKey() {
243 char maskingKeyChunk[4]{};
244
245 const std::size_t ret = readFrameData(maskingKeyChunk, maskingKeyNumBytesLeft);
246
247 const std::size_t cursor = static_cast<std::size_t>(maskingKeyNumBytes - maskingKeyNumBytesLeft);
248 for (std::size_t i = 0; i < ret; i++) {
249 maskingKey[cursor + i] = maskingKeyChunk[i];
251 }
252
253 if (maskingKeyNumBytesLeft == 0) {
254 if (payloadNumBytes > 0) {
255 parserState = ParserState::PAYLOAD;
256 } else {
257 if (fin) {
259 }
260 reset();
261 }
262 }
263
264 return ret;
265 }
266
267 std::size_t Receiver::readPayload() {
268 char payloadChunk[MAX_PAYLOAD_CHUNK_LEN]{};
269
270 const std::size_t payloadChunkLeft = (MAX_PAYLOAD_CHUNK_LEN <= payloadNumBytesLeft)
271 ? static_cast<std::size_t>(MAX_PAYLOAD_CHUNK_LEN)
272 : static_cast<std::size_t>(payloadNumBytesLeft);
273 const std::size_t ret = readFrameData(payloadChunk, payloadChunkLeft);
274
275 if (ret > 0) {
276 const std::size_t payloadChunkLen = static_cast<std::size_t>(ret);
277
278 if (masked) {
279 for (std::size_t i = 0; i < payloadChunkLen; i++) {
280 *(payloadChunk + i) = *(payloadChunk + i) ^ *(maskingKey + (i + payloadNumBytes - payloadNumBytesLeft) % 4);
281 }
282 }
283
284 LOG(TRACE) << "WebSocket receive: Frame data\n" << utils::hexDump(payloadChunk, payloadChunkLen, 32, true);
285
286 onMessageData(payloadChunk, payloadChunkLen);
287
288 payloadNumBytesLeft -= payloadChunkLen;
289 payloadTotalRead += payloadChunkLen;
290 }
291
292 if (payloadNumBytesLeft == 0) {
293 if (fin) {
295 }
296 reset();
297 }
298
299 return ret;
300 }
301
302 void Receiver::reset() {
303 parserState = ParserState::BEGIN;
304
305 fin = false;
306 continuation = false;
307 masked = false;
308
309 opCode = 0;
310
311 elengthNumBytes = 0;
313
314 payloadNumBytes = 0;
316
319
320 errorState = 0;
321 }
322
323} // namespace web::websocket
#define MAX_PAYLOAD_CHUNK_LEN
Definition Receiver.cpp:55
std::size_t readELength2()
Definition Receiver.cpp:180
virtual void onMessageEnd()=0
std::size_t receive()
Definition Receiver.cpp:67
virtual void onMessageStart(int opCode)=0
std::size_t readLength()
Definition Receiver.cpp:138
std::size_t readMaskingKey()
Definition Receiver.cpp:242
std::size_t readELength()
Definition Receiver.cpp:216
Receiver(bool maskingExpected)
Definition Receiver.cpp:60
std::size_t readFrameData(char *chunk, std::size_t chunkLen)
Definition Receiver.cpp:108
virtual void onMessageData(const char *chunk, uint64_t chunkLen)=0
virtual std::size_t readFrameChunk(char *chunk, std::size_t chunkLen) const =0
virtual void onMessageError(uint16_t errnum)=0
std::size_t readPayload()
Definition Receiver.cpp:267
std::size_t readOpcode()
Definition Receiver.cpp:112
std::size_t readELength8()
Definition Receiver.cpp:198
std::size_t payloadTotalRead
Definition Receiver.h:122
std::size_t getPayloadTotalRead() const
Definition Receiver.cpp:104
std::string hexDump(const char *bytes, uint64_t length, int prefixLength, bool prefixAtFirstLine)
Definition hexdump.cpp:66