1//
2// WebSocketImpl.cpp
3//
4// Library: Net
5// Package: WebSocket
6// Module: WebSocketImpl
7//
8// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#define NOMINMAX
16#include "Poco/Net/WebSocketImpl.h"
17#include "Poco/Net/NetException.h"
18#include "Poco/Net/WebSocket.h"
19#include "Poco/Net/HTTPSession.h"
20#include "Poco/Buffer.h"
21#include "Poco/BinaryWriter.h"
22#include "Poco/BinaryReader.h"
23#include "Poco/MemoryStream.h"
24#include "Poco/Format.h"
25#include <limits>
26#include <cstring>
27
28
29namespace Poco {
30namespace Net {
31
32
33WebSocketImpl::WebSocketImpl(StreamSocketImpl* pStreamSocketImpl, HTTPSession& session, bool mustMaskPayload):
34 StreamSocketImpl(pStreamSocketImpl->sockfd()),
35 _pStreamSocketImpl(pStreamSocketImpl),
36 _maxPayloadSize(std::numeric_limits<int>::max()),
37 _buffer(0),
38 _bufferOffset(0),
39 _frameFlags(0),
40 _mustMaskPayload(mustMaskPayload)
41{
42 poco_check_ptr(pStreamSocketImpl);
43 _pStreamSocketImpl->duplicate();
44 session.drainBuffer(_buffer);
45}
46
47
48WebSocketImpl::~WebSocketImpl()
49{
50 try
51 {
52 _pStreamSocketImpl->release();
53 reset();
54 }
55 catch (...)
56 {
57 poco_unexpected();
58 }
59}
60
61
62int WebSocketImpl::sendBytes(const void* buffer, int length, int flags)
63{
64 Poco::Buffer<char> frame(length + MAX_HEADER_LENGTH);
65 Poco::MemoryOutputStream ostr(frame.begin(), frame.size());
66 Poco::BinaryWriter writer(ostr, Poco::BinaryWriter::NETWORK_BYTE_ORDER);
67
68 if (flags == 0) flags = WebSocket::FRAME_BINARY;
69 flags &= 0xff;
70 writer << static_cast<Poco::UInt8>(flags);
71 Poco::UInt8 lengthByte(0);
72 if (_mustMaskPayload)
73 {
74 lengthByte |= FRAME_FLAG_MASK;
75 }
76 if (length < 126)
77 {
78 lengthByte |= static_cast<Poco::UInt8>(length);
79 writer << lengthByte;
80 }
81 else if (length < 65536)
82 {
83 lengthByte |= 126;
84 writer << lengthByte << static_cast<Poco::UInt16>(length);
85 }
86 else
87 {
88 lengthByte |= 127;
89 writer << lengthByte << static_cast<Poco::UInt64>(length);
90 }
91 if (_mustMaskPayload)
92 {
93 const Poco::UInt32 mask = _rnd.next();
94 const char* m = reinterpret_cast<const char*>(&mask);
95 const char* b = reinterpret_cast<const char*>(buffer);
96 writer.writeRaw(m, 4);
97 char* p = frame.begin() + ostr.charsWritten();
98 for (int i = 0; i < length; i++)
99 {
100 p[i] = b[i] ^ m[i % 4];
101 }
102 }
103 else
104 {
105 std::memcpy(frame.begin() + ostr.charsWritten(), buffer, length);
106 }
107 _pStreamSocketImpl->sendBytes(frame.begin(), length + static_cast<int>(ostr.charsWritten()));
108 return length;
109}
110
111
112int WebSocketImpl::receiveHeader(char mask[4], bool& useMask)
113{
114 char header[MAX_HEADER_LENGTH];
115 int n = receiveNBytes(header, 2);
116 if (n <= 0)
117 {
118 _frameFlags = 0;
119 return n;
120 }
121 poco_assert (n == 2);
122 Poco::UInt8 flags = static_cast<Poco::UInt8>(header[0]);
123 _frameFlags = flags;
124 Poco::UInt8 lengthByte = static_cast<Poco::UInt8>(header[1]);
125 useMask = ((lengthByte & FRAME_FLAG_MASK) != 0);
126 int payloadLength;
127 lengthByte &= 0x7f;
128 if (lengthByte == 127)
129 {
130 n = receiveNBytes(header + 2, 8);
131 if (n <= 0)
132 {
133 _frameFlags = 0;
134 return n;
135 }
136 Poco::MemoryInputStream istr(header + 2, 8);
137 Poco::BinaryReader reader(istr, Poco::BinaryReader::NETWORK_BYTE_ORDER);
138 Poco::UInt64 l;
139 reader >> l;
140 if (l > _maxPayloadSize) throw WebSocketException("Payload too big", WebSocket::WS_ERR_PAYLOAD_TOO_BIG);
141 payloadLength = static_cast<int>(l);
142 }
143 else if (lengthByte == 126)
144 {
145 n = receiveNBytes(header + 2, 2);
146 if (n <= 0)
147 {
148 _frameFlags = 0;
149 return n;
150 }
151 Poco::MemoryInputStream istr(header + 2, 2);
152 Poco::BinaryReader reader(istr, Poco::BinaryReader::NETWORK_BYTE_ORDER);
153 Poco::UInt16 l;
154 reader >> l;
155 if (l > _maxPayloadSize) throw WebSocketException("Payload too big", WebSocket::WS_ERR_PAYLOAD_TOO_BIG);
156 payloadLength = static_cast<int>(l);
157 }
158 else
159 {
160 if (lengthByte > _maxPayloadSize) throw WebSocketException("Payload too big", WebSocket::WS_ERR_PAYLOAD_TOO_BIG);
161 payloadLength = lengthByte;
162 }
163
164 if (useMask)
165 {
166 n = receiveNBytes(mask, 4);
167 if (n <= 0)
168 {
169 _frameFlags = 0;
170 return n;
171 }
172 }
173
174 return payloadLength;
175}
176
177
178void WebSocketImpl::setMaxPayloadSize(int maxPayloadSize)
179{
180 poco_assert (maxPayloadSize > 0);
181
182 _maxPayloadSize = maxPayloadSize;
183}
184
185
186int WebSocketImpl::receivePayload(char *buffer, int payloadLength, char mask[4], bool useMask)
187{
188 int received = receiveNBytes(reinterpret_cast<char*>(buffer), payloadLength);
189 if (received <= 0) throw WebSocketException("Incomplete frame received", WebSocket::WS_ERR_INCOMPLETE_FRAME);
190
191 if (useMask)
192 {
193 for (int i = 0; i < received; i++)
194 {
195 buffer[i] ^= mask[i % 4];
196 }
197 }
198 return received;
199}
200
201
202int WebSocketImpl::receiveBytes(void* buffer, int length, int)
203{
204 char mask[4];
205 bool useMask;
206 int payloadLength = receiveHeader(mask, useMask);
207 if (payloadLength <= 0)
208 return payloadLength;
209 if (payloadLength > length)
210 throw WebSocketException(Poco::format("Insufficient buffer for payload size %d", payloadLength), WebSocket::WS_ERR_PAYLOAD_TOO_BIG);
211 return receivePayload(reinterpret_cast<char*>(buffer), payloadLength, mask, useMask);
212}
213
214
215int WebSocketImpl::receiveBytes(Poco::Buffer<char>& buffer, int, const Poco::Timespan&)
216{
217 char mask[4];
218 bool useMask;
219 int payloadLength = receiveHeader(mask, useMask);
220 if (payloadLength <= 0)
221 return payloadLength;
222 std::size_t oldSize = buffer.size();
223 buffer.resize(oldSize + payloadLength);
224 return receivePayload(buffer.begin() + oldSize, payloadLength, mask, useMask);
225}
226
227
228int WebSocketImpl::receiveNBytes(void* buffer, int bytes)
229{
230 int received = receiveSomeBytes(reinterpret_cast<char*>(buffer), bytes);
231 if (received > 0)
232 {
233 while (received < bytes)
234 {
235 int n = receiveSomeBytes(reinterpret_cast<char*>(buffer) + received, bytes - received);
236 if (n > 0)
237 received += n;
238 else
239 throw WebSocketException("Incomplete frame received", WebSocket::WS_ERR_INCOMPLETE_FRAME);
240 }
241 }
242 return received;
243}
244
245
246int WebSocketImpl::receiveSomeBytes(char* buffer, int bytes)
247{
248 int n = static_cast<int>(_buffer.size()) - _bufferOffset;
249 if (n > 0)
250 {
251 if (bytes < n) n = bytes;
252 std::memcpy(buffer, _buffer.begin() + _bufferOffset, n);
253 _bufferOffset += n;
254 return n;
255 }
256 else
257 {
258 return _pStreamSocketImpl->receiveBytes(buffer, bytes);
259 }
260}
261
262
263SocketImpl* WebSocketImpl::acceptConnection(SocketAddress& /*clientAddr*/)
264{
265 throw Poco::InvalidAccessException("Cannot acceptConnection() on a WebSocketImpl");
266}
267
268
269void WebSocketImpl::connect(const SocketAddress& /*address*/)
270{
271 throw Poco::InvalidAccessException("Cannot connect() a WebSocketImpl");
272}
273
274
275void WebSocketImpl::connect(const SocketAddress& /*address*/, const Poco::Timespan& /*timeout*/)
276{
277 throw Poco::InvalidAccessException("Cannot connect() a WebSocketImpl");
278}
279
280
281void WebSocketImpl::connectNB(const SocketAddress& /*address*/)
282{
283 throw Poco::InvalidAccessException("Cannot connectNB() a WebSocketImpl");
284}
285
286
287void WebSocketImpl::bind(const SocketAddress& /*address*/, bool /*reuseAddress*/)
288{
289 throw Poco::InvalidAccessException("Cannot bind() a WebSocketImpl");
290}
291
292
293void WebSocketImpl::bind(const SocketAddress& /*address*/, bool /*reuseAddress*/, bool /*reusePort*/)
294{
295 throw Poco::InvalidAccessException("Cannot bind() a WebSocketImpl");
296}
297
298
299void WebSocketImpl::bind6(const SocketAddress& /*address*/, bool /*reuseAddress*/, bool /*ipV6Only*/)
300{
301 throw Poco::InvalidAccessException("Cannot bind6() a WebSocketImpl");
302}
303
304
305void WebSocketImpl::bind6(const SocketAddress& /*address*/, bool /*reuseAddress*/, bool /*reusePort*/, bool /*ipV6Only*/)
306{
307 throw Poco::InvalidAccessException("Cannot bind6() a WebSocketImpl");
308}
309
310
311void WebSocketImpl::listen(int /*backlog*/)
312{
313 throw Poco::InvalidAccessException("Cannot listen() on a WebSocketImpl");
314}
315
316
317void WebSocketImpl::close()
318{
319 _pStreamSocketImpl->close();
320 reset();
321}
322
323
324void WebSocketImpl::shutdownReceive()
325{
326 _pStreamSocketImpl->shutdownReceive();
327}
328
329
330void WebSocketImpl::shutdownSend()
331{
332 _pStreamSocketImpl->shutdownSend();
333}
334
335
336void WebSocketImpl::shutdown()
337{
338 _pStreamSocketImpl->shutdown();
339}
340
341
342int WebSocketImpl::sendTo(const void* /*buffer*/, int /*length*/, const SocketAddress& /*address*/, int /*flags*/)
343{
344 throw Poco::InvalidAccessException("Cannot sendTo() on a WebSocketImpl");
345}
346
347
348int WebSocketImpl::receiveFrom(void* /*buffer*/, int /*length*/, SocketAddress& /*address*/, int /*flags*/)
349{
350 throw Poco::InvalidAccessException("Cannot receiveFrom() on a WebSocketImpl");
351}
352
353
354void WebSocketImpl::sendUrgent(unsigned char /*data*/)
355{
356 throw Poco::InvalidAccessException("Cannot sendUrgent() on a WebSocketImpl");
357}
358
359
360bool WebSocketImpl::secure() const
361{
362 return _pStreamSocketImpl->secure();
363}
364
365
366void WebSocketImpl::setSendTimeout(const Poco::Timespan& timeout)
367{
368 _pStreamSocketImpl->setSendTimeout(timeout);
369}
370
371
372Poco::Timespan WebSocketImpl::getSendTimeout()
373{
374 return _pStreamSocketImpl->getSendTimeout();
375}
376
377
378void WebSocketImpl::setReceiveTimeout(const Poco::Timespan& timeout)
379{
380 _pStreamSocketImpl->setReceiveTimeout(timeout);
381}
382
383
384Poco::Timespan WebSocketImpl::getReceiveTimeout()
385{
386 return _pStreamSocketImpl->getReceiveTimeout();
387}
388
389
390int WebSocketImpl::available()
391{
392 int n = static_cast<int>(_buffer.size()) - _bufferOffset;
393 if (n > 0)
394 return n + _pStreamSocketImpl->available();
395 else
396 return _pStreamSocketImpl->available();
397}
398
399
400} } // namespace Poco::Net
401