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 | |
29 | namespace Poco { |
30 | namespace Net { |
31 | |
32 | |
33 | WebSocketImpl::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 | |
48 | WebSocketImpl::~WebSocketImpl() |
49 | { |
50 | try |
51 | { |
52 | _pStreamSocketImpl->release(); |
53 | reset(); |
54 | } |
55 | catch (...) |
56 | { |
57 | poco_unexpected(); |
58 | } |
59 | } |
60 | |
61 | |
62 | int 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 | |
112 | int WebSocketImpl::(char mask[4], bool& useMask) |
113 | { |
114 | char [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 | |
178 | void WebSocketImpl::setMaxPayloadSize(int maxPayloadSize) |
179 | { |
180 | poco_assert (maxPayloadSize > 0); |
181 | |
182 | _maxPayloadSize = maxPayloadSize; |
183 | } |
184 | |
185 | |
186 | int 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 | |
202 | int 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 | |
215 | int 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 | |
228 | int 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 | |
246 | int 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 | |
263 | SocketImpl* WebSocketImpl::acceptConnection(SocketAddress& /*clientAddr*/) |
264 | { |
265 | throw Poco::InvalidAccessException("Cannot acceptConnection() on a WebSocketImpl" ); |
266 | } |
267 | |
268 | |
269 | void WebSocketImpl::connect(const SocketAddress& /*address*/) |
270 | { |
271 | throw Poco::InvalidAccessException("Cannot connect() a WebSocketImpl" ); |
272 | } |
273 | |
274 | |
275 | void WebSocketImpl::connect(const SocketAddress& /*address*/, const Poco::Timespan& /*timeout*/) |
276 | { |
277 | throw Poco::InvalidAccessException("Cannot connect() a WebSocketImpl" ); |
278 | } |
279 | |
280 | |
281 | void WebSocketImpl::connectNB(const SocketAddress& /*address*/) |
282 | { |
283 | throw Poco::InvalidAccessException("Cannot connectNB() a WebSocketImpl" ); |
284 | } |
285 | |
286 | |
287 | void WebSocketImpl::bind(const SocketAddress& /*address*/, bool /*reuseAddress*/) |
288 | { |
289 | throw Poco::InvalidAccessException("Cannot bind() a WebSocketImpl" ); |
290 | } |
291 | |
292 | |
293 | void WebSocketImpl::bind(const SocketAddress& /*address*/, bool /*reuseAddress*/, bool /*reusePort*/) |
294 | { |
295 | throw Poco::InvalidAccessException("Cannot bind() a WebSocketImpl" ); |
296 | } |
297 | |
298 | |
299 | void WebSocketImpl::bind6(const SocketAddress& /*address*/, bool /*reuseAddress*/, bool /*ipV6Only*/) |
300 | { |
301 | throw Poco::InvalidAccessException("Cannot bind6() a WebSocketImpl" ); |
302 | } |
303 | |
304 | |
305 | void WebSocketImpl::bind6(const SocketAddress& /*address*/, bool /*reuseAddress*/, bool /*reusePort*/, bool /*ipV6Only*/) |
306 | { |
307 | throw Poco::InvalidAccessException("Cannot bind6() a WebSocketImpl" ); |
308 | } |
309 | |
310 | |
311 | void WebSocketImpl::listen(int /*backlog*/) |
312 | { |
313 | throw Poco::InvalidAccessException("Cannot listen() on a WebSocketImpl" ); |
314 | } |
315 | |
316 | |
317 | void WebSocketImpl::close() |
318 | { |
319 | _pStreamSocketImpl->close(); |
320 | reset(); |
321 | } |
322 | |
323 | |
324 | void WebSocketImpl::shutdownReceive() |
325 | { |
326 | _pStreamSocketImpl->shutdownReceive(); |
327 | } |
328 | |
329 | |
330 | void WebSocketImpl::shutdownSend() |
331 | { |
332 | _pStreamSocketImpl->shutdownSend(); |
333 | } |
334 | |
335 | |
336 | void WebSocketImpl::shutdown() |
337 | { |
338 | _pStreamSocketImpl->shutdown(); |
339 | } |
340 | |
341 | |
342 | int 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 | |
348 | int WebSocketImpl::receiveFrom(void* /*buffer*/, int /*length*/, SocketAddress& /*address*/, int /*flags*/) |
349 | { |
350 | throw Poco::InvalidAccessException("Cannot receiveFrom() on a WebSocketImpl" ); |
351 | } |
352 | |
353 | |
354 | void WebSocketImpl::sendUrgent(unsigned char /*data*/) |
355 | { |
356 | throw Poco::InvalidAccessException("Cannot sendUrgent() on a WebSocketImpl" ); |
357 | } |
358 | |
359 | |
360 | bool WebSocketImpl::secure() const |
361 | { |
362 | return _pStreamSocketImpl->secure(); |
363 | } |
364 | |
365 | |
366 | void WebSocketImpl::setSendTimeout(const Poco::Timespan& timeout) |
367 | { |
368 | _pStreamSocketImpl->setSendTimeout(timeout); |
369 | } |
370 | |
371 | |
372 | Poco::Timespan WebSocketImpl::getSendTimeout() |
373 | { |
374 | return _pStreamSocketImpl->getSendTimeout(); |
375 | } |
376 | |
377 | |
378 | void WebSocketImpl::setReceiveTimeout(const Poco::Timespan& timeout) |
379 | { |
380 | _pStreamSocketImpl->setReceiveTimeout(timeout); |
381 | } |
382 | |
383 | |
384 | Poco::Timespan WebSocketImpl::getReceiveTimeout() |
385 | { |
386 | return _pStreamSocketImpl->getReceiveTimeout(); |
387 | } |
388 | |
389 | |
390 | int 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 | |