SNode.C
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages Concepts
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/*
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/Transmitter.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
56constexpr int WSMAXFRAMEPAYLOADLENGTH = 1024;
57
58namespace web::websocket {
59
60 Transmitter::Transmitter(core::socket::stream::SocketConnection* socketConnection, bool masking)
61 : socketConnection(socketConnection)
62 , masking(masking) {
63 }
64
66 }
67
68 void Transmitter::sendMessage(uint8_t opCode, const char* message, std::size_t messageLength) {
69 send(true, opCode, message, messageLength);
70 }
71
72 void Transmitter::sendMessageStart(uint8_t opCode, const char* message, std::size_t messageLength) {
73 send(false, opCode, message, messageLength);
74 }
75
76 void Transmitter::sendMessageFrame(const char* message, std::size_t messageLength) {
77 send(false, 0, message, messageLength);
78 }
79
80 void Transmitter::sendMessageEnd(const char* message, std::size_t messageLength) {
81 send(true, 0, message, messageLength);
82 }
83
84 void Transmitter::send(bool end, uint8_t opCode, const char* message, std::size_t messageLength) {
85 std::size_t messageOffset = 0;
86
87 do {
88 const std::size_t sendFrameLength =
89 (messageLength - messageOffset <= WSMAXFRAMEPAYLOADLENGTH) ? messageLength - messageOffset : WSMAXFRAMEPAYLOADLENGTH;
90
91 const bool fin = (sendFrameLength == messageLength - messageOffset) && end;
92
93 sendFrame(fin, opCode, message + messageOffset, sendFrameLength);
94
95 messageOffset += sendFrameLength;
96
97 opCode = 0; // continuation
98 } while (messageLength - messageOffset > 0);
99 }
100
101 void Transmitter::sendFrame(bool fin, uint8_t opCode, const char* payload, uint64_t payloadLength) {
102 uint64_t length = 0;
103
104 if (payloadLength < 126) {
105 length = payloadLength;
106 } else if (payloadLength < 0x10000) {
107 length = 126;
108 } else {
109 length = 127;
110 }
111
112 char header[2];
113 header[0] = static_cast<char>((fin ? 0b10000000 : 0) | opCode);
114
115 header[1] = static_cast<char>((masking ? 0b10000000 : 0) | length);
116
117 sendFrameData(header, 2);
118
119 switch (length) {
120 case 126:
121 sendFrameData(static_cast<uint16_t>(payloadLength));
122 break;
123 case 127:
124 sendFrameData(payloadLength);
125 break;
126 }
127
128 union MaskingKey {
129 uint32_t keyAsValue;
130 char keyAsBytes[4];
131 };
132
133 MaskingKey maskingKeyAsArray = {.keyAsValue = distribution(randomDevice)};
134
135 if (payloadLength > 0) {
136 LOG(TRACE) << "WebSocket send: Frame data\n" << utils::hexDump(payload, payloadLength, 32, true);
137 }
138
139 if (masking) {
140 sendFrameData(maskingKeyAsArray.keyAsBytes, 4);
141
142 for (uint64_t i = 0; i < payloadLength; i++) {
143 *(const_cast<char*>(payload) + i) = static_cast<char>(*(payload + i) ^ *(maskingKeyAsArray.keyAsBytes + i % 4));
144 }
145 }
146
147 sendFrameData(payload, payloadLength);
148
149 if (masking) {
150 for (uint64_t i = 0; i < payloadLength; i++) {
151 *(const_cast<char*>(payload) + i) = static_cast<char>(*(payload + i) ^ *(maskingKeyAsArray.keyAsBytes + i % 4));
152 }
153 }
154 }
155
156 void Transmitter::sendFrameData(uint8_t data) const {
157 if (!closeSent) {
158 sendFrameData(reinterpret_cast<char*>(&data), sizeof(uint8_t));
159 }
160 }
161
162 void Transmitter::sendFrameData(uint16_t data) const {
163 if (!closeSent) {
164 uint16_t sendData = htobe16(data);
165 sendFrameData(reinterpret_cast<char*>(&sendData), sizeof(uint16_t));
166 }
167 }
168
169 void Transmitter::sendFrameData(uint32_t data) const {
170 if (!closeSent) {
171 uint32_t sendData = htobe32(data);
172 sendFrameData(reinterpret_cast<char*>(&sendData), sizeof(uint32_t));
173 }
174 }
175
176 void Transmitter::sendFrameData(uint64_t data) const {
177 if (!closeSent) {
178 uint64_t sendData = htobe64(data);
179 sendFrameData(reinterpret_cast<char*>(&sendData), sizeof(uint64_t));
180 }
181 }
182
183 void Transmitter::sendFrameData(const char* frame, uint64_t frameLength) const {
184 if (!closeSent) {
185 uint64_t frameOffset = 0;
186
187 do {
188 std::size_t sendChunkLen =
189 (frameLength - frameOffset <= SIZE_MAX) ? static_cast<std::size_t>(frameLength - frameOffset) : SIZE_MAX;
190
191 socketConnection->sendToPeer(frame + frameOffset, sendChunkLen);
192
193 frameOffset += sendChunkLen;
194 } while (frameLength - frameOffset > 0);
195 }
196 }
197
198} // namespace web::websocket
constexpr int WSMAXFRAMEPAYLOADLENGTH
virtual void sendToPeer(const char *chunk, std::size_t chunkLen)=0
void sendMessageEnd(const char *message, std::size_t messageLength)
void sendFrameData(const char *frame, uint64_t frameLength) const
std::random_device randomDevice
Definition Transmitter.h:90
void sendFrameData(uint8_t data) const
void sendFrameData(uint16_t data) const
void sendMessage(uint8_t opCode, const char *message, std::size_t messageLength)
void sendFrameData(uint32_t data) const
Transmitter(core::socket::stream::SocketConnection *socketConnection, bool masking)
core::socket::stream::SocketConnection * socketConnection
Definition Transmitter.h:93
void send(bool end, uint8_t opCode, const char *message, std::size_t messageLength)
void sendFrame(bool fin, uint8_t opCode, const char *payload, uint64_t payloadLength)
void sendFrameData(uint64_t data) const
std::uniform_int_distribution< uint32_t > distribution
Definition Transmitter.h:91
void sendMessageStart(uint8_t opCode, const char *message, std::size_t messageLength)
void sendMessageFrame(const char *message, std::size_t messageLength)
std::string hexDump(const char *bytes, uint64_t length, int prefixLength, bool prefixAtFirstLine)
Definition hexdump.cpp:66