SNode.C
Loading...
Searching...
No Matches
Transmitter.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#include "web/websocket/Transmitter.h"
21
22#include "core/socket/stream/SocketConnection.h"
23
24#ifndef DOXYGEN_SHOULD_SKIP_THIS
25
26#include "log/Logger.h"
27#include "utils/hexdump.h"
28
29#include <endian.h>
30#include <string>
31
32#endif /* DOXYGEN_SHOULD_SKIP_THIS */
33
34constexpr int WSMAXFRAMEPAYLOADLENGTH = 1024;
35
36namespace web::websocket {
37
38 Transmitter::Transmitter(core::socket::stream::SocketConnection* socketConnection, bool masking)
39 : socketConnection(socketConnection)
40 , masking(masking) {
41 }
42
43 Transmitter::~Transmitter() {
44 }
45
46 void Transmitter::sendMessage(uint8_t opCode, const char* message, std::size_t messageLength) {
47 send(true, opCode, message, messageLength);
48 }
49
50 void Transmitter::sendMessageStart(uint8_t opCode, const char* message, std::size_t messageLength) {
51 send(false, opCode, message, messageLength);
52 }
53
54 void Transmitter::sendMessageFrame(const char* message, std::size_t messageLength) {
55 send(false, 0, message, messageLength);
56 }
57
58 void Transmitter::sendMessageEnd(const char* message, std::size_t messageLength) {
59 send(true, 0, message, messageLength);
60 }
61
62 void Transmitter::send(bool end, uint8_t opCode, const char* message, std::size_t messageLength) {
63 std::size_t messageOffset = 0;
64
65 do {
66 const std::size_t sendFrameLength =
67 (messageLength - messageOffset <= WSMAXFRAMEPAYLOADLENGTH) ? messageLength - messageOffset : WSMAXFRAMEPAYLOADLENGTH;
68
69 const bool fin = (sendFrameLength == messageLength - messageOffset) && end;
70
71 sendFrame(fin, opCode, message + messageOffset, sendFrameLength);
72
73 messageOffset += sendFrameLength;
74
75 opCode = 0; // continuation
76 } while (messageLength - messageOffset > 0);
77 }
78
79 void Transmitter::sendFrame(bool fin, uint8_t opCode, const char* payload, uint64_t payloadLength) {
80 uint64_t length = 0;
81
82 if (payloadLength < 126) {
83 length = payloadLength;
84 } else if (payloadLength < 0x10000) {
85 length = 126;
86 } else {
87 length = 127;
88 }
89
90 char header[2];
91 header[0] = static_cast<char>((fin ? 0b10000000 : 0) | opCode);
92
93 header[1] = static_cast<char>((masking ? 0b10000000 : 0) | length);
94
95 sendFrameData(header, 2);
96
97 switch (length) {
98 case 126:
99 sendFrameData(static_cast<uint16_t>(payloadLength));
100 break;
101 case 127:
102 sendFrameData(payloadLength);
103 break;
104 }
105
106 union MaskingKey {
107 uint32_t keyAsValue;
108 char keyAsBytes[4];
109 };
110
111 MaskingKey maskingKeyAsArray = {.keyAsValue = distribution(randomDevice)};
112
113 if (payloadLength > 0) {
114 LOG(TRACE) << "WebSocket send: Frame data\n" << utils::hexDump(payload, payloadLength, 32, true);
115 }
116
117 if (masking) {
118 sendFrameData(htobe32(maskingKeyAsArray.keyAsValue));
119
120 for (uint64_t i = 0; i < payloadLength; i++) {
121 *(const_cast<char*>(payload) + i) = static_cast<char>(*(payload + i) ^ *(maskingKeyAsArray.keyAsBytes + i % 4));
122 }
123 }
124
125 sendFrameData(payload, payloadLength);
126
127 if (masking) {
128 for (uint64_t i = 0; i < payloadLength; i++) {
129 *(const_cast<char*>(payload) + i) = static_cast<char>(*(payload + i) ^ *(maskingKeyAsArray.keyAsBytes + i % 4));
130 }
131 }
132 }
133
134 void Transmitter::sendFrameData(uint8_t data) const {
135 if (!closeSent) {
136 sendFrameData(reinterpret_cast<char*>(&data), sizeof(uint8_t));
137 }
138 }
139
140 void Transmitter::sendFrameData(uint16_t data) const {
141 if (!closeSent) {
142 uint16_t sendData = htobe16(data);
143 sendFrameData(reinterpret_cast<char*>(&sendData), sizeof(uint16_t));
144 }
145 }
146
147 void Transmitter::sendFrameData(uint32_t data) const {
148 if (!closeSent) {
149 uint32_t sendData = htobe32(data);
150 sendFrameData(reinterpret_cast<char*>(&sendData), sizeof(uint32_t));
151 }
152 }
153
154 void Transmitter::sendFrameData(uint64_t data) const {
155 if (!closeSent) {
156 uint64_t sendData = htobe64(data);
157 sendFrameData(reinterpret_cast<char*>(&sendData), sizeof(uint64_t));
158 }
159 }
160
161 void Transmitter::sendFrameData(const char* frame, uint64_t frameLength) const {
162 if (!closeSent) {
163 uint64_t frameOffset = 0;
164
165 do {
166 std::size_t sendChunkLen =
167 (frameLength - frameOffset <= SIZE_MAX) ? static_cast<std::size_t>(frameLength - frameOffset) : SIZE_MAX;
168
169 socketConnection->sendToPeer(frame + frameOffset, sendChunkLen);
170
171 frameOffset += sendChunkLen;
172 } while (frameLength - frameOffset > 0);
173 }
174 }
175
176} // namespace web::websocket
constexpr int WSMAXFRAMEPAYLOADLENGTH