1/****************************************************************************
2**
3** Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWebSockets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qwebsocket.h"
41#include "qwebsocket_p.h"
42#include "qwebsocketprotocol_p.h"
43#include "qwebsockethandshakerequest_p.h"
44#include "qwebsockethandshakeresponse_p.h"
45#include "qdefaultmaskgenerator_p.h"
46
47#include <QtCore/QUrl>
48#include <QtNetwork/QAuthenticator>
49#include <QtNetwork/QTcpSocket>
50#include <QtCore/QByteArray>
51#include <QtCore/QtEndian>
52#include <QtCore/QCryptographicHash>
53#include <QtCore/QRegularExpression>
54#include <QtCore/QStringList>
55#include <QtNetwork/QHostAddress>
56#include <QtCore/QStringBuilder> //for more efficient string concatenation
57#ifndef QT_NONETWORKPROXY
58#include <QtNetwork/QNetworkProxy>
59#endif
60#ifndef QT_NO_SSL
61#include <QtNetwork/QSslConfiguration>
62#include <QtNetwork/QSslError>
63#include <QtNetwork/QSslPreSharedKeyAuthenticator>
64#endif
65
66#include <QtCore/QDebug>
67
68#include <limits>
69
70QT_BEGIN_NAMESPACE
71
72const quint64 MAX_OUTGOING_FRAME_SIZE_IN_BYTES = std::numeric_limits<int>::max() - 1;
73const quint64 DEFAULT_OUTGOING_FRAME_SIZE_IN_BYTES = 512 * 512 * 2; //default size of a frame when sending a message
74
75QWebSocketConfiguration::QWebSocketConfiguration() :
76#ifndef QT_NO_SSL
77 m_ignoredSslErrors(),
78 m_ignoreSslErrors(false),
79#endif
80#ifndef QT_NO_NETWORKPROXY
81 m_proxy(QNetworkProxy::DefaultProxy),
82#endif
83 m_pSocket(nullptr)
84{
85}
86
87/*!
88 \internal
89*/
90QWebSocketPrivate::QWebSocketPrivate(const QString &origin, QWebSocketProtocol::Version version) :
91 QObjectPrivate(),
92 m_pSocket(nullptr),
93 m_errorString(),
94 m_version(version),
95 m_resourceName(),
96 m_request(),
97 m_origin(origin),
98 m_protocol(),
99 m_extension(),
100 m_socketState(QAbstractSocket::UnconnectedState),
101 m_pauseMode(QAbstractSocket::PauseNever),
102 m_readBufferSize(0),
103 m_key(),
104 m_mustMask(true),
105 m_isClosingHandshakeSent(false),
106 m_isClosingHandshakeReceived(false),
107 m_closeCode(QWebSocketProtocol::CloseCodeNormal),
108 m_closeReason(),
109 m_pingTimer(),
110 m_configuration(),
111 m_pMaskGenerator(&m_defaultMaskGenerator),
112 m_defaultMaskGenerator(),
113 m_handshakeState(NothingDoneState),
114 m_outgoingFrameSize(DEFAULT_OUTGOING_FRAME_SIZE_IN_BYTES)
115{
116 m_pingTimer.start();
117}
118
119/*!
120 \internal
121*/
122QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version) :
123 QObjectPrivate(),
124 m_pSocket(pTcpSocket),
125 m_errorString(pTcpSocket->errorString()),
126 m_version(version),
127 m_resourceName(),
128 m_request(),
129 m_origin(),
130 m_protocol(),
131 m_extension(),
132 m_socketState(pTcpSocket->state()),
133 m_pauseMode(pTcpSocket->pauseMode()),
134 m_readBufferSize(pTcpSocket->readBufferSize()),
135 m_key(),
136 m_mustMask(true),
137 m_isClosingHandshakeSent(false),
138 m_isClosingHandshakeReceived(false),
139 m_closeCode(QWebSocketProtocol::CloseCodeNormal),
140 m_closeReason(),
141 m_pingTimer(),
142 m_configuration(),
143 m_pMaskGenerator(&m_defaultMaskGenerator),
144 m_defaultMaskGenerator(),
145 m_handshakeState(NothingDoneState),
146 m_outgoingFrameSize(DEFAULT_OUTGOING_FRAME_SIZE_IN_BYTES)
147{
148 m_pingTimer.start();
149}
150
151/*!
152 \internal
153*/
154void QWebSocketPrivate::init()
155{
156 Q_ASSERT(q_ptr);
157 Q_ASSERT(m_pMaskGenerator);
158
159 m_dataProcessor->setParent(q_ptr);
160 m_pMaskGenerator->seed();
161
162 if (m_pSocket) {
163 makeConnections(pTcpSocket: m_pSocket);
164 }
165}
166
167/*!
168 \internal
169*/
170QWebSocketPrivate::~QWebSocketPrivate()
171{
172}
173
174/*!
175 \internal
176*/
177void QWebSocketPrivate::closeGoingAway()
178{
179 if (!m_pSocket)
180 return;
181 if (state() == QAbstractSocket::ConnectedState)
182 close(closeCode: QWebSocketProtocol::CloseCodeGoingAway, reason: QWebSocket::tr(s: "Connection closed"));
183 releaseConnections(pTcpSocket: m_pSocket);
184}
185
186/*!
187 \internal
188 */
189void QWebSocketPrivate::abort()
190{
191 if (m_pSocket)
192 m_pSocket->abort();
193}
194
195/*!
196 \internal
197 */
198QAbstractSocket::SocketError QWebSocketPrivate::error() const
199{
200 QAbstractSocket::SocketError err = QAbstractSocket::UnknownSocketError;
201 if (Q_LIKELY(m_pSocket))
202 err = m_pSocket->error();
203 return err;
204}
205
206/*!
207 \internal
208 */
209QString QWebSocketPrivate::errorString() const
210{
211 QString errMsg;
212 if (!m_errorString.isEmpty())
213 errMsg = m_errorString;
214 else if (m_pSocket)
215 errMsg = m_pSocket->errorString();
216 return errMsg;
217}
218
219/*!
220 \internal
221 */
222bool QWebSocketPrivate::flush()
223{
224 bool result = true;
225 if (Q_LIKELY(m_pSocket))
226 result = m_pSocket->flush();
227 return result;
228}
229
230#ifndef Q_OS_WASM
231
232/*!
233 \internal
234 */
235qint64 QWebSocketPrivate::sendTextMessage(const QString &message)
236{
237 return doWriteFrames(data: message.toUtf8(), isBinary: false);
238}
239
240/*!
241 \internal
242 */
243qint64 QWebSocketPrivate::sendBinaryMessage(const QByteArray &data)
244{
245 return doWriteFrames(data, isBinary: true);
246}
247
248#endif
249
250#ifndef QT_NO_SSL
251/*!
252 \internal
253 */
254void QWebSocketPrivate::setSslConfiguration(const QSslConfiguration &sslConfiguration)
255{
256 m_configuration.m_sslConfiguration = sslConfiguration;
257}
258
259/*!
260 \internal
261 */
262QSslConfiguration QWebSocketPrivate::sslConfiguration() const
263{
264 return m_configuration.m_sslConfiguration;
265}
266
267/*!
268 \internal
269 */
270void QWebSocketPrivate::ignoreSslErrors(const QList<QSslError> &errors)
271{
272 m_configuration.m_ignoredSslErrors = errors;
273 if (Q_LIKELY(m_pSocket)) {
274 QSslSocket *pSslSocket = qobject_cast<QSslSocket *>(object: m_pSocket);
275 if (Q_LIKELY(pSslSocket))
276 pSslSocket->ignoreSslErrors(errors);
277 }
278}
279
280/*!
281 * \internal
282 */
283void QWebSocketPrivate::ignoreSslErrors()
284{
285 m_configuration.m_ignoreSslErrors = true;
286 if (Q_LIKELY(m_pSocket)) {
287 QSslSocket *pSslSocket = qobject_cast<QSslSocket *>(object: m_pSocket);
288 if (Q_LIKELY(pSslSocket))
289 pSslSocket->ignoreSslErrors();
290 }
291}
292
293/*!
294* \internal
295*/
296void QWebSocketPrivate::_q_updateSslConfiguration()
297{
298 if (QSslSocket *sslSock = qobject_cast<QSslSocket *>(object: m_pSocket))
299 m_configuration.m_sslConfiguration = sslSock->sslConfiguration();
300}
301
302#endif
303
304/*!
305 Called from QWebSocketServer
306 \internal
307 */
308QWebSocket *QWebSocketPrivate::upgradeFrom(QTcpSocket *pTcpSocket,
309 const QWebSocketHandshakeRequest &request,
310 const QWebSocketHandshakeResponse &response,
311 QObject *parent)
312{
313 QWebSocket *pWebSocket = new QWebSocket(pTcpSocket, response.acceptedVersion(), parent);
314 if (Q_LIKELY(pWebSocket)) {
315 QNetworkRequest netRequest(request.requestUrl());
316 const auto headers = request.headers();
317 for (auto it = headers.begin(), end = headers.end(); it != end; ++it)
318 netRequest.setRawHeader(headerName: it.key().toLatin1(), value: it.value().toLatin1());
319#ifndef QT_NO_SSL
320 if (QSslSocket *sslSock = qobject_cast<QSslSocket *>(object: pTcpSocket))
321 pWebSocket->setSslConfiguration(sslSock->sslConfiguration());
322#endif
323 pWebSocket->d_func()->setExtension(response.acceptedExtension());
324 pWebSocket->d_func()->setOrigin(request.origin());
325 pWebSocket->d_func()->setRequest(netRequest);
326 pWebSocket->d_func()->setProtocol(response.acceptedProtocol());
327 pWebSocket->d_func()->setResourceName(request.requestUrl().toString(options: QUrl::RemoveUserInfo));
328 //a server should not send masked frames
329 pWebSocket->d_func()->enableMasking(enable: false);
330 }
331
332 return pWebSocket;
333}
334
335#ifndef Q_OS_WASM
336
337/*!
338 \internal
339 */
340void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString reason)
341{
342 if (Q_UNLIKELY(!m_pSocket))
343 return;
344 if (!m_isClosingHandshakeSent) {
345 Q_Q(QWebSocket);
346 m_closeCode = closeCode;
347 // 125 is the maximum length of a control frame, and 2 bytes are used for the close code:
348 const QByteArray reasonUtf8 = reason.toUtf8().left(len: 123);
349 m_closeReason = QString::fromUtf8(str: reasonUtf8);
350 const quint16 code = qToBigEndian<quint16>(source: closeCode);
351 QByteArray payload;
352 payload.append(s: static_cast<const char *>(static_cast<const void *>(&code)), len: 2);
353 if (!reasonUtf8.isEmpty())
354 payload.append(a: reasonUtf8);
355 quint32 maskingKey = 0;
356 if (m_mustMask) {
357 maskingKey = generateMaskingKey();
358 QWebSocketProtocol::mask(payload: payload.data(), size: quint64(payload.size()), maskingKey);
359 }
360 QByteArray frame = getFrameHeader(opCode: QWebSocketProtocol::OpCodeClose,
361 payloadLength: quint64(payload.size()), maskingKey, lastFrame: true);
362
363 Q_ASSERT(payload.length() <= 125);
364 frame.append(a: payload);
365 m_pSocket->write(data: frame);
366 m_pSocket->flush();
367
368 m_isClosingHandshakeSent = true;
369
370 Q_EMIT q->aboutToClose();
371 }
372 m_pSocket->close();
373}
374
375/*!
376 \internal
377 */
378void QWebSocketPrivate::open(const QNetworkRequest &request, bool mask)
379{
380 //just delete the old socket for the moment;
381 //later, we can add more 'intelligent' handling by looking at the URL
382
383 Q_Q(QWebSocket);
384 QUrl url = request.url();
385 if (!url.isValid() || url.toString().contains(QStringLiteral("\r\n"))) {
386 setErrorString(QWebSocket::tr(s: "Invalid URL."));
387 Q_EMIT q->error(error: QAbstractSocket::ConnectionRefusedError);
388 return;
389 }
390 if (m_pSocket) {
391 releaseConnections(pTcpSocket: m_pSocket);
392 m_pSocket->deleteLater();
393 m_pSocket = nullptr;
394 }
395 //if (m_url != url)
396 if (Q_LIKELY(!m_pSocket)) {
397 m_dataProcessor->clear();
398 m_isClosingHandshakeReceived = false;
399 m_isClosingHandshakeSent = false;
400
401 setRequest(request);
402 QString resourceName = url.path(options: QUrl::FullyEncoded);
403 // Check for encoded \r\n
404 if (resourceName.contains(QStringLiteral("%0D%0A"))) {
405 setRequest(QNetworkRequest()); //clear request
406 setErrorString(QWebSocket::tr(s: "Invalid resource name."));
407 Q_EMIT q->error(error: QAbstractSocket::ConnectionRefusedError);
408 return;
409 }
410 if (!url.query().isEmpty()) {
411 if (!resourceName.endsWith(c: QChar::fromLatin1(c: '?'))) {
412 resourceName.append(c: QChar::fromLatin1(c: '?'));
413 }
414 resourceName.append(s: url.query(QUrl::FullyEncoded));
415 }
416 if (resourceName.isEmpty())
417 resourceName = QStringLiteral("/");
418 setResourceName(resourceName);
419 enableMasking(enable: mask);
420
421 #ifndef QT_NO_SSL
422 if (url.scheme() == QStringLiteral("wss")) {
423 if (!QSslSocket::supportsSsl()) {
424 const QString message =
425 QWebSocket::tr(s: "SSL Sockets are not supported on this platform.");
426 setErrorString(message);
427 Q_EMIT q->error(error: QAbstractSocket::UnsupportedSocketOperationError);
428 } else {
429 QSslSocket *sslSocket = new QSslSocket(q);
430 m_pSocket = sslSocket;
431 if (Q_LIKELY(m_pSocket)) {
432 QObject::connect(sender: sslSocket, signal: &QSslSocket::connected, slot: [sslSocket](){
433 sslSocket->setSocketOption(option: QAbstractSocket::LowDelayOption, value: 1);
434 sslSocket->setSocketOption(option: QAbstractSocket::KeepAliveOption, value: 1);
435 });
436 m_pSocket->setReadBufferSize(m_readBufferSize);
437 m_pSocket->setPauseMode(m_pauseMode);
438
439 makeConnections(pTcpSocket: m_pSocket);
440 setSocketState(QAbstractSocket::ConnectingState);
441
442 sslSocket->setSslConfiguration(m_configuration.m_sslConfiguration);
443 if (Q_UNLIKELY(m_configuration.m_ignoreSslErrors))
444 sslSocket->ignoreSslErrors();
445 else
446 sslSocket->ignoreSslErrors(errors: m_configuration.m_ignoredSslErrors);
447 #ifndef QT_NO_NETWORKPROXY
448 sslSocket->setProxy(m_configuration.m_proxy);
449 m_pSocket->setProtocolTag(QStringLiteral("https"));
450 #endif
451 sslSocket->connectToHostEncrypted(hostName: url.host(), port: quint16(url.port(defaultPort: 443)));
452 } else {
453 const QString message = QWebSocket::tr(s: "Out of memory.");
454 setErrorString(message);
455 Q_EMIT q->error(error: QAbstractSocket::SocketResourceError);
456 }
457 }
458 } else
459 #endif
460 if (url.scheme() == QStringLiteral("ws")) {
461 m_pSocket = new QTcpSocket(q);
462 if (Q_LIKELY(m_pSocket)) {
463 QObject::connect(sender: m_pSocket, signal: &QTcpSocket::connected, slot: [this](){
464 m_pSocket->setSocketOption(option: QAbstractSocket::LowDelayOption, value: 1);
465 m_pSocket->setSocketOption(option: QAbstractSocket::KeepAliveOption, value: 1);
466 });
467 m_pSocket->setReadBufferSize(m_readBufferSize);
468 m_pSocket->setPauseMode(m_pauseMode);
469
470 makeConnections(pTcpSocket: m_pSocket);
471 setSocketState(QAbstractSocket::ConnectingState);
472 #ifndef QT_NO_NETWORKPROXY
473 m_pSocket->setProxy(m_configuration.m_proxy);
474 m_pSocket->setProtocolTag(QStringLiteral("http"));
475 #endif
476 m_pSocket->connectToHost(hostName: url.host(), port: quint16(url.port(defaultPort: 80)));
477 } else {
478 const QString message = QWebSocket::tr(s: "Out of memory.");
479 setErrorString(message);
480 Q_EMIT q->error(error: QAbstractSocket::SocketResourceError);
481 }
482 } else {
483 const QString message =
484 QWebSocket::tr(s: "Unsupported WebSocket scheme: %1").arg(a: url.scheme());
485 setErrorString(message);
486 Q_EMIT q->error(error: QAbstractSocket::UnsupportedSocketOperationError);
487 }
488 }
489}
490
491#endif
492
493/*!
494 \internal
495 */
496void QWebSocketPrivate::ping(const QByteArray &payload)
497{
498 QByteArray payloadTruncated = payload.left(len: 125);
499 m_pingTimer.restart();
500 quint32 maskingKey = 0;
501 if (m_mustMask)
502 maskingKey = generateMaskingKey();
503 QByteArray pingFrame = getFrameHeader(opCode: QWebSocketProtocol::OpCodePing,
504 payloadLength: quint64(payloadTruncated.size()),
505 maskingKey, lastFrame: true);
506 if (m_mustMask)
507 QWebSocketProtocol::mask(payload: &payloadTruncated, maskingKey);
508 pingFrame.append(a: payloadTruncated);
509 qint64 ret = writeFrame(frame: pingFrame);
510 Q_UNUSED(ret);
511}
512
513/*!
514 \internal
515 Sets the version to use for the WebSocket protocol;
516 this must be set before the socket is opened.
517*/
518void QWebSocketPrivate::setVersion(QWebSocketProtocol::Version version)
519{
520 if (m_version != version)
521 m_version = version;
522}
523
524/*!
525 \internal
526 Sets the resource name of the connection; must be set before the socket is openend
527*/
528void QWebSocketPrivate::setResourceName(const QString &resourceName)
529{
530 if (m_resourceName != resourceName)
531 m_resourceName = resourceName;
532}
533
534/*!
535 \internal
536 */
537void QWebSocketPrivate::setRequest(const QNetworkRequest &request)
538{
539 if (m_request != request)
540 m_request = request;
541}
542
543/*!
544 \internal
545 */
546void QWebSocketPrivate::setOrigin(const QString &origin)
547{
548 if (m_origin != origin)
549 m_origin = origin;
550}
551
552/*!
553 \internal
554 */
555void QWebSocketPrivate::setProtocol(const QString &protocol)
556{
557 if (m_protocol != protocol)
558 m_protocol = protocol;
559}
560
561/*!
562 \internal
563 */
564void QWebSocketPrivate::setExtension(const QString &extension)
565{
566 if (m_extension != extension)
567 m_extension = extension;
568}
569
570/*!
571 \internal
572 */
573void QWebSocketPrivate::enableMasking(bool enable)
574{
575 if (m_mustMask != enable)
576 m_mustMask = enable;
577}
578
579/*!
580 * \internal
581 */
582void QWebSocketPrivate::makeConnections(QTcpSocket *pTcpSocket)
583{
584 Q_ASSERT(pTcpSocket);
585 Q_Q(QWebSocket);
586
587 if (Q_LIKELY(pTcpSocket)) {
588 //pass through signals
589 QObject::connect(sender: pTcpSocket, signal: &QAbstractSocket::errorOccurred,
590 receiver: q, slot: QOverload<QAbstractSocket::SocketError>::of(ptr: &QWebSocket::error));
591#ifndef QT_NO_NETWORKPROXY
592 QObject::connect(sender: pTcpSocket, signal: &QAbstractSocket::proxyAuthenticationRequired, receiver: q,
593 slot: &QWebSocket::proxyAuthenticationRequired);
594#endif // QT_NO_NETWORKPROXY
595 QObject::connect(sender: pTcpSocket, signal: &QAbstractSocket::readChannelFinished, receiver: q,
596 slot: &QWebSocket::readChannelFinished);
597 QObject::connect(sender: pTcpSocket, signal: &QAbstractSocket::aboutToClose, receiver: q, slot: &QWebSocket::aboutToClose);
598
599 QObjectPrivate::connect(sender: pTcpSocket, signal: &QObject::destroyed,
600 receiverPrivate: this, slot: &QWebSocketPrivate::socketDestroyed);
601
602 //catch signals
603 QObjectPrivate::connect(sender: pTcpSocket, signal: &QAbstractSocket::stateChanged, receiverPrivate: this,
604 slot: &QWebSocketPrivate::processStateChanged);
605 //!!!important to use a QueuedConnection here;
606 //with QTcpSocket there is no problem, but with QSslSocket the processing hangs
607 QObjectPrivate::connect(sender: pTcpSocket, signal: &QAbstractSocket::readyRead, receiverPrivate: this,
608 slot: &QWebSocketPrivate::processData, type: Qt::QueuedConnection);
609#ifndef QT_NO_SSL
610 const QSslSocket * const sslSocket = qobject_cast<const QSslSocket *>(object: pTcpSocket);
611 if (sslSocket) {
612 QObject::connect(sender: sslSocket, signal: &QSslSocket::preSharedKeyAuthenticationRequired, receiver: q,
613 slot: &QWebSocket::preSharedKeyAuthenticationRequired);
614 QObject::connect(sender: sslSocket, signal: &QSslSocket::encryptedBytesWritten, receiver: q,
615 slot: &QWebSocket::bytesWritten);
616 QObjectPrivate::connect(sender: sslSocket,
617 signal: QOverload<const QList<QSslError>&>::of(ptr: &QSslSocket::sslErrors),
618 receiverPrivate: this, slot: &QWebSocketPrivate::_q_updateSslConfiguration);
619 QObject::connect(sender: sslSocket,
620 signal: QOverload<const QList<QSslError>&>::of(ptr: &QSslSocket::sslErrors),
621 receiver: q, slot: &QWebSocket::sslErrors);
622 QObjectPrivate::connect(sender: sslSocket, signal: &QSslSocket::encrypted,
623 receiverPrivate: this, slot: &QWebSocketPrivate::_q_updateSslConfiguration);
624 } else
625#endif // QT_NO_SSL
626 {
627 QObject::connect(sender: pTcpSocket, signal: &QAbstractSocket::bytesWritten, receiver: q,
628 slot: &QWebSocket::bytesWritten);
629 }
630 }
631
632 QObject::connect(sender: m_dataProcessor, signal: &QWebSocketDataProcessor::textFrameReceived, receiver: q,
633 slot: &QWebSocket::textFrameReceived);
634 QObject::connect(sender: m_dataProcessor, signal: &QWebSocketDataProcessor::binaryFrameReceived, receiver: q,
635 slot: &QWebSocket::binaryFrameReceived);
636 QObject::connect(sender: m_dataProcessor, signal: &QWebSocketDataProcessor::binaryMessageReceived, receiver: q,
637 slot: &QWebSocket::binaryMessageReceived);
638 QObject::connect(sender: m_dataProcessor, signal: &QWebSocketDataProcessor::textMessageReceived, receiver: q,
639 slot: &QWebSocket::textMessageReceived);
640 QObjectPrivate::connect(sender: m_dataProcessor, signal: &QWebSocketDataProcessor::errorEncountered, receiverPrivate: this,
641 slot: &QWebSocketPrivate::close);
642 QObjectPrivate::connect(sender: m_dataProcessor, signal: &QWebSocketDataProcessor::pingReceived, receiverPrivate: this,
643 slot: &QWebSocketPrivate::processPing);
644 QObjectPrivate::connect(sender: m_dataProcessor, signal: &QWebSocketDataProcessor::pongReceived, receiverPrivate: this,
645 slot: &QWebSocketPrivate::processPong);
646 QObjectPrivate::connect(sender: m_dataProcessor, signal: &QWebSocketDataProcessor::closeReceived, receiverPrivate: this,
647 slot: &QWebSocketPrivate::processClose);
648
649 //fire readyread, in case we already have data inside the tcpSocket
650 if (pTcpSocket->bytesAvailable())
651 Q_EMIT pTcpSocket->readyRead();
652}
653
654/*!
655 * \internal
656 */
657void QWebSocketPrivate::releaseConnections(const QTcpSocket *pTcpSocket)
658{
659 if (Q_LIKELY(pTcpSocket))
660 pTcpSocket->disconnect();
661 m_dataProcessor->disconnect();
662}
663
664/*!
665 \internal
666 */
667QWebSocketProtocol::Version QWebSocketPrivate::version() const
668{
669 return m_version;
670}
671
672/*!
673 \internal
674 */
675QString QWebSocketPrivate::resourceName() const
676{
677 return m_resourceName;
678}
679
680/*!
681 \internal
682 */
683QNetworkRequest QWebSocketPrivate::request() const
684{
685 return m_request;
686}
687
688/*!
689 \internal
690 */
691QString QWebSocketPrivate::origin() const
692{
693 return m_origin;
694}
695
696/*!
697 \internal
698 */
699QString QWebSocketPrivate::protocol() const
700{
701 return m_protocol;
702}
703
704/*!
705 \internal
706 */
707QString QWebSocketPrivate::extension() const
708{
709 return m_extension;
710}
711
712/*!
713 * \internal
714 */
715QWebSocketProtocol::CloseCode QWebSocketPrivate::closeCode() const
716{
717 return m_closeCode;
718}
719
720/*!
721 * \internal
722 */
723QString QWebSocketPrivate::closeReason() const
724{
725 return m_closeReason;
726}
727
728/*!
729 * \internal
730 */
731QByteArray QWebSocketPrivate::getFrameHeader(QWebSocketProtocol::OpCode opCode,
732 quint64 payloadLength, quint32 maskingKey,
733 bool lastFrame)
734{
735 Q_Q(QWebSocket);
736 QByteArray header;
737 bool ok = payloadLength <= 0x7FFFFFFFFFFFFFFFULL;
738
739 if (Q_LIKELY(ok)) {
740 //FIN, RSV1-3, opcode (RSV-1, RSV-2 and RSV-3 are zero)
741 quint8 byte = static_cast<quint8>((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00));
742 header.append(c: static_cast<char>(byte));
743
744 byte = 0x00;
745 if (maskingKey != 0)
746 byte |= 0x80;
747 if (payloadLength <= 125) {
748 byte |= static_cast<quint8>(payloadLength);
749 header.append(c: static_cast<char>(byte));
750 } else if (payloadLength <= 0xFFFFU) {
751 byte |= 126;
752 header.append(c: static_cast<char>(byte));
753 quint16 swapped = qToBigEndian<quint16>(source: static_cast<quint16>(payloadLength));
754 header.append(s: static_cast<const char *>(static_cast<const void *>(&swapped)), len: 2);
755 } else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL) {
756 byte |= 127;
757 header.append(c: static_cast<char>(byte));
758 quint64 swapped = qToBigEndian<quint64>(source: payloadLength);
759 header.append(s: static_cast<const char *>(static_cast<const void *>(&swapped)), len: 8);
760 }
761
762 if (maskingKey != 0) {
763 const quint32 mask = qToBigEndian<quint32>(source: maskingKey);
764 header.append(s: static_cast<const char *>(static_cast<const void *>(&mask)),
765 len: sizeof(quint32));
766 }
767 } else {
768 setErrorString(QStringLiteral("WebSocket::getHeader: payload too big!"));
769 Q_EMIT q->error(error: QAbstractSocket::DatagramTooLargeError);
770 }
771
772 return header;
773}
774
775/*!
776 * \internal
777 */
778qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary)
779{
780 qint64 payloadWritten = 0;
781 if (Q_UNLIKELY(!m_pSocket) || (state() != QAbstractSocket::ConnectedState))
782 return payloadWritten;
783
784 Q_Q(QWebSocket);
785 const QWebSocketProtocol::OpCode firstOpCode = isBinary ?
786 QWebSocketProtocol::OpCodeBinary : QWebSocketProtocol::OpCodeText;
787
788 int numFrames = data.size() / int(outgoingFrameSize());
789 QByteArray tmpData(data);
790 tmpData.detach();
791 char *payload = tmpData.data();
792 quint64 sizeLeft = quint64(data.size()) % outgoingFrameSize();
793 if (Q_LIKELY(sizeLeft))
794 ++numFrames;
795
796 //catch the case where the payload is zero bytes;
797 //in this case, we still need to send a frame
798 if (Q_UNLIKELY(numFrames == 0))
799 numFrames = 1;
800 quint64 currentPosition = 0;
801 quint64 bytesLeft = quint64(data.size());
802
803 for (int i = 0; i < numFrames; ++i) {
804 quint32 maskingKey = 0;
805 if (m_mustMask)
806 maskingKey = generateMaskingKey();
807
808 const bool isLastFrame = (i == (numFrames - 1));
809 const bool isFirstFrame = (i == 0);
810
811 const quint64 size = qMin(a: bytesLeft, b: outgoingFrameSize());
812 const QWebSocketProtocol::OpCode opcode = isFirstFrame ? firstOpCode
813 : QWebSocketProtocol::OpCodeContinue;
814
815 //write header
816 m_pSocket->write(data: getFrameHeader(opCode: opcode, payloadLength: size, maskingKey, lastFrame: isLastFrame));
817
818 //write payload
819 if (Q_LIKELY(size > 0)) {
820 char *currentData = payload + currentPosition;
821 if (m_mustMask)
822 QWebSocketProtocol::mask(payload: currentData, size, maskingKey);
823 qint64 written = m_pSocket->write(data: currentData, len: static_cast<qint64>(size));
824 if (Q_LIKELY(written > 0)) {
825 payloadWritten += written;
826 } else {
827 m_pSocket->flush();
828 setErrorString(QWebSocket::tr(s: "Error writing bytes to socket: %1.")
829 .arg(a: m_pSocket->errorString()));
830 Q_EMIT q->error(error: QAbstractSocket::NetworkError);
831 break;
832 }
833 }
834 currentPosition += size;
835 bytesLeft -= size;
836 }
837 if (Q_UNLIKELY(payloadWritten != data.size())) {
838 setErrorString(QWebSocket::tr(s: "Bytes written %1 != %2.")
839 .arg(a: payloadWritten).arg(a: data.size()));
840 Q_EMIT q->error(error: QAbstractSocket::NetworkError);
841 }
842 return payloadWritten;
843}
844
845/*!
846 \internal
847 */
848quint32 QWebSocketPrivate::generateMaskingKey() const
849{
850 return m_pMaskGenerator->nextMask();
851}
852
853/*!
854 \internal
855 */
856QByteArray QWebSocketPrivate::generateKey() const
857{
858 QByteArray key;
859
860 for (int i = 0; i < 4; ++i) {
861 const quint32 tmp = m_pMaskGenerator->nextMask();
862 key.append(s: static_cast<const char *>(static_cast<const void *>(&tmp)), len: sizeof(quint32));
863 }
864
865 return key.toBase64();
866}
867
868
869/*!
870 \internal
871 */
872QString QWebSocketPrivate::calculateAcceptKey(const QByteArray &key) const
873{
874 const QByteArray tmpKey = key + QByteArrayLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
875 const QByteArray hash = QCryptographicHash::hash(data: tmpKey, method: QCryptographicHash::Sha1).toBase64();
876 return QString::fromLatin1(str: hash);
877}
878
879/*!
880 \internal
881 */
882qint64 QWebSocketPrivate::writeFrames(const QList<QByteArray> &frames)
883{
884 qint64 written = 0;
885 if (Q_LIKELY(m_pSocket)) {
886 QList<QByteArray>::const_iterator it;
887 for (it = frames.cbegin(); it < frames.cend(); ++it)
888 written += writeFrame(frame: *it);
889 }
890 return written;
891}
892
893/*!
894 \internal
895 */
896qint64 QWebSocketPrivate::writeFrame(const QByteArray &frame)
897{
898 qint64 written = 0;
899 if (Q_LIKELY(m_pSocket))
900 written = m_pSocket->write(data: frame);
901 return written;
902}
903
904/*!
905 \internal
906 */
907static QString readLine(QTcpSocket *pSocket)
908{
909 Q_ASSERT(pSocket);
910 QString line;
911 char c;
912 while (pSocket->getChar(c: &c)) {
913 if (c == char('\r')) {
914 pSocket->getChar(c: &c);
915 break;
916 } else {
917 line.append(c: QChar::fromLatin1(c));
918 }
919 }
920 return line;
921}
922
923// this function is a copy of QHttpNetworkReplyPrivate::parseStatus
924static bool parseStatusLine(const QByteArray &status, int *majorVersion, int *minorVersion,
925 int *statusCode, QString *reasonPhrase)
926{
927 // from RFC 2616:
928 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
929 // HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
930 // that makes: 'HTTP/n.n xxx Message'
931 // byte count: 0123456789012
932
933 static const int minLength = 11;
934 static const int dotPos = 6;
935 static const int spacePos = 8;
936 static const char httpMagic[] = "HTTP/";
937
938 if (status.length() < minLength
939 || !status.startsWith(c: httpMagic)
940 || status.at(i: dotPos) != '.'
941 || status.at(i: spacePos) != ' ') {
942 // I don't know how to parse this status line
943 return false;
944 }
945
946 // optimize for the valid case: defer checking until the end
947 *majorVersion = status.at(i: dotPos - 1) - '0';
948 *minorVersion = status.at(i: dotPos + 1) - '0';
949
950 int i = spacePos;
951 int j = status.indexOf(c: ' ', from: i + 1); // j == -1 || at(j) == ' ' so j+1 == 0 && j+1 <= length()
952 const QByteArray code = status.mid(index: i + 1, len: j - i - 1);
953
954 bool ok;
955 *statusCode = code.toInt(ok: &ok);
956 *reasonPhrase = QString::fromLatin1(str: status.constData() + j + 1);
957
958 return ok && uint(*majorVersion) <= 9 && uint(* minorVersion) <= 9;
959}
960
961
962//called on the client for a server handshake response
963/*!
964 \internal
965 */
966void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket)
967{
968 Q_Q(QWebSocket);
969 if (Q_UNLIKELY(!pSocket))
970 return;
971 // Reset handshake on a new connection.
972 if (m_handshakeState == AllDoneState)
973 m_handshakeState = NothingDoneState;
974
975 QString errorDescription;
976
977 switch (m_handshakeState) {
978 case NothingDoneState:
979 m_headers.clear();
980 m_handshakeState = ReadingStatusState;
981 Q_FALLTHROUGH();
982 case ReadingStatusState:
983 if (!pSocket->canReadLine())
984 return;
985 m_statusLine = pSocket->readLine().trimmed();
986 if (Q_UNLIKELY(!parseStatusLine(m_statusLine, &m_httpMajorVersion, &m_httpMinorVersion, &m_httpStatusCode, &m_httpStatusMessage))) {
987 errorDescription = QWebSocket::tr(s: "Invalid statusline in response: %1.").arg(a: QString::fromLatin1(str: m_statusLine));
988 break;
989 }
990 m_handshakeState = ReadingHeaderState;
991 Q_FALLTHROUGH();
992 case ReadingHeaderState: {
993 // TODO: this should really use the existing code from QHttpNetworkReplyPrivate::parseHeader
994 auto lastHeader = m_headers.end();
995 while (pSocket->canReadLine()) {
996 QString headerLine = readLine(pSocket);
997
998 if (headerLine.isEmpty()) {
999 // end of headers
1000 m_handshakeState = ParsingHeaderState;
1001 break;
1002 } else if (headerLine.startsWith(c: QLatin1Char(' ')) || headerLine.startsWith(c: QLatin1Char('\t'))) {
1003 // continuation line -- add this to the last header field
1004 if (Q_UNLIKELY(lastHeader == m_headers.end())) {
1005 errorDescription = QWebSocket::tr(s: "Malformed header in response: %1.").arg(a: headerLine);
1006 break;
1007 }
1008 lastHeader.value().append(c: QLatin1Char(' '));
1009 lastHeader.value().append(s: headerLine.trimmed());
1010 } else {
1011 int colonPos = headerLine.indexOf(c: QLatin1Char(':'));
1012 if (Q_UNLIKELY(colonPos <= 0)) {
1013 errorDescription = QWebSocket::tr(s: "Malformed header in response: %1.").arg(a: headerLine);
1014 break;
1015 }
1016 lastHeader = m_headers.insert(akey: headerLine.left(n: colonPos).trimmed().toLower(),
1017 avalue: headerLine.mid(position: colonPos + 1).trimmed());
1018 }
1019 }
1020
1021 if (m_handshakeState != ParsingHeaderState) {
1022 if (pSocket->state() != QAbstractSocket::ConnectedState) {
1023 errorDescription = QWebSocket::tr(s: "QWebSocketPrivate::processHandshake: Connection closed while reading header.");
1024 break;
1025 }
1026 return;
1027 }
1028 Q_FALLTHROUGH();
1029 }
1030 case ParsingHeaderState: {
1031 const QString acceptKey = m_headers.value(QStringLiteral("sec-websocket-accept"), adefaultValue: QString());
1032 const QString upgrade = m_headers.value(QStringLiteral("upgrade"), adefaultValue: QString());
1033 const QString connection = m_headers.value(QStringLiteral("connection"), adefaultValue: QString());
1034// unused for the moment
1035// const QString extensions = m_headers.value(QStringLiteral("sec-websocket-extensions"),
1036// QString());
1037// const QString protocol = m_headers.value(QStringLiteral("sec-websocket-protocol"),
1038// QString());
1039 const QString version = m_headers.value(QStringLiteral("sec-websocket-version"), adefaultValue: QString());
1040
1041 bool ok = false;
1042 if (Q_LIKELY(m_httpStatusCode == 101)) {
1043 //HTTP/x.y 101 Switching Protocols
1044 //TODO: do not check the httpStatusText right now
1045 ok = !(acceptKey.isEmpty() ||
1046 (m_httpMajorVersion < 1 || m_httpMinorVersion < 1) ||
1047 (upgrade.toLower() != QStringLiteral("websocket")) ||
1048 (connection.toLower() != QStringLiteral("upgrade")));
1049 if (ok) {
1050 const QString accept = calculateAcceptKey(key: m_key);
1051 ok = (accept == acceptKey);
1052 if (!ok)
1053 errorDescription =
1054 QWebSocket::tr(s: "Accept-Key received from server %1 does not match the client key %2.")
1055 .arg(a1: acceptKey, a2: accept);
1056 } else {
1057 errorDescription =
1058 QWebSocket::tr(s: "QWebSocketPrivate::processHandshake: Invalid statusline in response: %1.")
1059 .arg(a: QString::fromLatin1(str: m_statusLine));
1060 }
1061 } else if (m_httpStatusCode == 400) {
1062 //HTTP/1.1 400 Bad Request
1063 if (!version.isEmpty()) {
1064 const QStringList versions = version.split(QStringLiteral(", "), behavior: Qt::SkipEmptyParts);
1065 if (!versions.contains(str: QString::number(QWebSocketProtocol::currentVersion()))) {
1066 //if needed to switch protocol version, then we are finished here
1067 //because we cannot handle other protocols than the RFC one (v13)
1068 errorDescription =
1069 QWebSocket::tr(s: "Handshake: Server requests a version that we don't support: %1.")
1070 .arg(a: versions.join(QStringLiteral(", ")));
1071 } else {
1072 //we tried v13, but something different went wrong
1073 errorDescription =
1074 QWebSocket::tr(s: "QWebSocketPrivate::processHandshake: Unknown error condition encountered. Aborting connection.");
1075 }
1076 } else {
1077 errorDescription =
1078 QWebSocket::tr(s: "QWebSocketPrivate::processHandshake: Unknown error condition encountered. Aborting connection.");
1079 }
1080 } else {
1081 errorDescription =
1082 QWebSocket::tr(s: "QWebSocketPrivate::processHandshake: Unhandled http status code: %1 (%2).")
1083 .arg(a: m_httpStatusCode).arg(a: m_httpStatusMessage);
1084 }
1085 if (ok)
1086 m_handshakeState = AllDoneState;
1087 break;
1088 }
1089 case AllDoneState:
1090 Q_UNREACHABLE();
1091 break;
1092 }
1093
1094 if (m_handshakeState == AllDoneState) {
1095 // handshake succeeded
1096 setSocketState(QAbstractSocket::ConnectedState);
1097 Q_EMIT q->connected();
1098 } else {
1099 // handshake failed
1100 m_handshakeState = AllDoneState;
1101 setErrorString(errorDescription);
1102 Q_EMIT q->error(error: QAbstractSocket::ConnectionRefusedError);
1103 }
1104}
1105
1106/*!
1107 \internal
1108 */
1109void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketState)
1110{
1111 Q_ASSERT(m_pSocket);
1112 Q_Q(QWebSocket);
1113 QAbstractSocket::SocketState webSocketState = this->state();
1114
1115 switch (socketState) {
1116 case QAbstractSocket::ConnectedState:
1117#ifndef QT_NO_SSL
1118 if (QSslSocket *sslSock = qobject_cast<QSslSocket *>(object: m_pSocket))
1119 m_configuration.m_sslConfiguration = sslSock->sslConfiguration();
1120#endif
1121 if (webSocketState == QAbstractSocket::ConnectingState) {
1122 m_key = generateKey();
1123
1124 QList<QPair<QString, QString> > headers;
1125 const auto headerList = m_request.rawHeaderList();
1126 for (const QByteArray &key : headerList)
1127 headers << qMakePair(x: QString::fromLatin1(str: key),
1128 y: QString::fromLatin1(str: m_request.rawHeader(headerName: key)));
1129
1130 const auto format = QUrl::RemoveScheme | QUrl::RemoveUserInfo
1131 | QUrl::RemovePath | QUrl::RemoveQuery
1132 | QUrl::RemoveFragment;
1133 const QString host = m_request.url().toString(options: format).mid(position: 2);
1134 const QString handshake = createHandShakeRequest(resourceName: m_resourceName,
1135 host,
1136 origin: origin(),
1137 extensions: QString(),
1138 protocols: QString(),
1139 key: m_key,
1140 headers);
1141 if (handshake.isEmpty()) {
1142 m_pSocket->abort();
1143 Q_EMIT q->error(error: QAbstractSocket::ConnectionRefusedError);
1144 return;
1145 }
1146 m_pSocket->write(data: handshake.toLatin1());
1147 }
1148 break;
1149
1150 case QAbstractSocket::ClosingState:
1151 if (webSocketState == QAbstractSocket::ConnectedState)
1152 setSocketState(QAbstractSocket::ClosingState);
1153 break;
1154
1155 case QAbstractSocket::UnconnectedState:
1156 if (webSocketState != QAbstractSocket::UnconnectedState) {
1157 setSocketState(QAbstractSocket::UnconnectedState);
1158 Q_EMIT q->disconnected();
1159 }
1160 break;
1161
1162 case QAbstractSocket::HostLookupState:
1163 case QAbstractSocket::ConnectingState:
1164 case QAbstractSocket::BoundState:
1165 case QAbstractSocket::ListeningState:
1166 //do nothing
1167 //to make C++ compiler happy;
1168 break;
1169 }
1170}
1171
1172void QWebSocketPrivate::socketDestroyed(QObject *socket)
1173{
1174 Q_ASSERT(m_pSocket);
1175 if (m_pSocket == socket)
1176 m_pSocket = nullptr;
1177}
1178
1179/*!
1180 \internal
1181 */
1182void QWebSocketPrivate::processData()
1183{
1184 if (!m_pSocket) // disconnected with data still in-bound
1185 return;
1186 while (m_pSocket->bytesAvailable()) {
1187 if (state() == QAbstractSocket::ConnectingState) {
1188 if (!m_pSocket->canReadLine())
1189 return;
1190 processHandshake(pSocket: m_pSocket);
1191 } else if (!m_dataProcessor->process(pIoDevice: m_pSocket)) {
1192 return;
1193 }
1194 }
1195}
1196
1197/*!
1198 \internal
1199 */
1200void QWebSocketPrivate::processPing(const QByteArray &data)
1201{
1202 Q_ASSERT(m_pSocket);
1203 quint32 maskingKey = 0;
1204 if (m_mustMask)
1205 maskingKey = generateMaskingKey();
1206 m_pSocket->write(data: getFrameHeader(opCode: QWebSocketProtocol::OpCodePong,
1207 payloadLength: unsigned(data.size()),
1208 maskingKey,
1209 lastFrame: true));
1210 if (data.size() > 0) {
1211 QByteArray maskedData = data;
1212 if (m_mustMask)
1213 QWebSocketProtocol::mask(payload: &maskedData, maskingKey);
1214 m_pSocket->write(data: maskedData);
1215 }
1216}
1217
1218/*!
1219 \internal
1220 */
1221void QWebSocketPrivate::processPong(const QByteArray &data)
1222{
1223 Q_Q(QWebSocket);
1224 Q_EMIT q->pong(elapsedTime: static_cast<quint64>(m_pingTimer.elapsed()), payload: data);
1225}
1226
1227/*!
1228 \internal
1229 */
1230void QWebSocketPrivate::processClose(QWebSocketProtocol::CloseCode closeCode, QString closeReason)
1231{
1232 m_isClosingHandshakeReceived = true;
1233 close(closeCode, reason: closeReason);
1234}
1235
1236/*!
1237 \internal
1238 */
1239QString QWebSocketPrivate::createHandShakeRequest(QString resourceName,
1240 QString host,
1241 QString origin,
1242 QString extensions,
1243 QString protocols,
1244 QByteArray key,
1245 const QList<QPair<QString, QString> > &headers)
1246{
1247 QStringList handshakeRequest;
1248 if (resourceName.contains(QStringLiteral("\r\n"))) {
1249 setErrorString(QWebSocket::tr(s: "The resource name contains newlines. " \
1250 "Possible attack detected."));
1251 return QString();
1252 }
1253 if (host.contains(QStringLiteral("\r\n"))) {
1254 setErrorString(QWebSocket::tr(s: "The hostname contains newlines. " \
1255 "Possible attack detected."));
1256 return QString();
1257 }
1258 if (origin.contains(QStringLiteral("\r\n"))) {
1259 setErrorString(QWebSocket::tr(s: "The origin contains newlines. " \
1260 "Possible attack detected."));
1261 return QString();
1262 }
1263 if (extensions.contains(QStringLiteral("\r\n"))) {
1264 setErrorString(QWebSocket::tr(s: "The extensions attribute contains newlines. " \
1265 "Possible attack detected."));
1266 return QString();
1267 }
1268 if (protocols.contains(QStringLiteral("\r\n"))) {
1269 setErrorString(QWebSocket::tr(s: "The protocols attribute contains newlines. " \
1270 "Possible attack detected."));
1271 return QString();
1272 }
1273
1274 handshakeRequest << QStringLiteral("GET ") % resourceName % QStringLiteral(" HTTP/1.1") <<
1275 QStringLiteral("Host: ") % host <<
1276 QStringLiteral("Upgrade: websocket") <<
1277 QStringLiteral("Connection: Upgrade") <<
1278 QStringLiteral("Sec-WebSocket-Key: ") % QString::fromLatin1(str: key);
1279 if (!origin.isEmpty())
1280 handshakeRequest << QStringLiteral("Origin: ") % origin;
1281 handshakeRequest << QStringLiteral("Sec-WebSocket-Version: ")
1282 % QString::number(QWebSocketProtocol::currentVersion());
1283 if (extensions.length() > 0)
1284 handshakeRequest << QStringLiteral("Sec-WebSocket-Extensions: ") % extensions;
1285 if (protocols.length() > 0)
1286 handshakeRequest << QStringLiteral("Sec-WebSocket-Protocol: ") % protocols;
1287
1288 for (const auto &header : headers)
1289 handshakeRequest << header.first % QStringLiteral(": ") % header.second;
1290
1291 handshakeRequest << QStringLiteral("\r\n");
1292
1293 return handshakeRequest.join(QStringLiteral("\r\n"));
1294}
1295
1296/*!
1297 \internal
1298 */
1299QAbstractSocket::SocketState QWebSocketPrivate::state() const
1300{
1301 return m_socketState;
1302}
1303
1304/*!
1305 \internal
1306 */
1307void QWebSocketPrivate::setSocketState(QAbstractSocket::SocketState state)
1308{
1309 Q_Q(QWebSocket);
1310 if (m_socketState != state) {
1311 m_socketState = state;
1312 Q_EMIT q->stateChanged(state: m_socketState);
1313 }
1314}
1315
1316/*!
1317 \internal
1318 */
1319void QWebSocketPrivate::setMaxAllowedIncomingFrameSize(quint64 maxAllowedIncomingFrameSize)
1320{
1321 m_dataProcessor->setMaxAllowedFrameSize(maxAllowedIncomingFrameSize);
1322}
1323
1324/*!
1325 \internal
1326 */
1327quint64 QWebSocketPrivate::maxAllowedIncomingFrameSize() const
1328{
1329 return m_dataProcessor->maxAllowedFrameSize();
1330}
1331
1332/*!
1333 \internal
1334 */
1335void QWebSocketPrivate::setMaxAllowedIncomingMessageSize(quint64 maxAllowedIncomingMessageSize)
1336{
1337 m_dataProcessor->setMaxAllowedMessageSize(maxAllowedIncomingMessageSize);
1338}
1339
1340/*!
1341 \internal
1342 */
1343quint64 QWebSocketPrivate::maxAllowedIncomingMessageSize() const
1344{
1345 return m_dataProcessor->maxAllowedMessageSize();
1346}
1347
1348/*!
1349 \internal
1350 */
1351quint64 QWebSocketPrivate::maxIncomingMessageSize()
1352{
1353 return QWebSocketDataProcessor::maxMessageSize();
1354}
1355
1356/*!
1357 \internal
1358 */
1359quint64 QWebSocketPrivate::maxIncomingFrameSize()
1360{
1361 return QWebSocketDataProcessor::maxFrameSize();
1362}
1363
1364/*!
1365 \internal
1366 */
1367void QWebSocketPrivate::setOutgoingFrameSize(quint64 outgoingFrameSize)
1368{
1369 if (outgoingFrameSize <= maxOutgoingFrameSize())
1370 m_outgoingFrameSize = outgoingFrameSize;
1371}
1372
1373/*!
1374 \internal
1375 */
1376quint64 QWebSocketPrivate::outgoingFrameSize() const
1377{
1378 return m_outgoingFrameSize;
1379}
1380
1381/*!
1382 \internal
1383 */
1384quint64 QWebSocketPrivate::maxOutgoingFrameSize()
1385{
1386 return MAX_OUTGOING_FRAME_SIZE_IN_BYTES;
1387}
1388
1389
1390/*!
1391 \internal
1392 */
1393void QWebSocketPrivate::setErrorString(const QString &errorString)
1394{
1395 if (m_errorString != errorString)
1396 m_errorString = errorString;
1397}
1398
1399/*!
1400 \internal
1401 */
1402QHostAddress QWebSocketPrivate::localAddress() const
1403{
1404 QHostAddress address;
1405 if (Q_LIKELY(m_pSocket))
1406 address = m_pSocket->localAddress();
1407 return address;
1408}
1409
1410/*!
1411 \internal
1412 */
1413quint16 QWebSocketPrivate::localPort() const
1414{
1415 quint16 port = 0;
1416 if (Q_LIKELY(m_pSocket))
1417 port = m_pSocket->localPort();
1418 return port;
1419}
1420
1421/*!
1422 \internal
1423 */
1424QAbstractSocket::PauseModes QWebSocketPrivate::pauseMode() const
1425{
1426 return m_pauseMode;
1427}
1428
1429/*!
1430 \internal
1431 */
1432QHostAddress QWebSocketPrivate::peerAddress() const
1433{
1434 QHostAddress address;
1435 if (Q_LIKELY(m_pSocket))
1436 address = m_pSocket->peerAddress();
1437 return address;
1438}
1439
1440/*!
1441 \internal
1442 */
1443QString QWebSocketPrivate::peerName() const
1444{
1445 QString name;
1446 if (Q_LIKELY(m_pSocket))
1447 name = m_pSocket->peerName();
1448 return name;
1449}
1450
1451/*!
1452 \internal
1453 */
1454quint16 QWebSocketPrivate::peerPort() const
1455{
1456 quint16 port = 0;
1457 if (Q_LIKELY(m_pSocket))
1458 port = m_pSocket->peerPort();
1459 return port;
1460}
1461
1462#ifndef QT_NO_NETWORKPROXY
1463/*!
1464 \internal
1465 */
1466QNetworkProxy QWebSocketPrivate::proxy() const
1467{
1468 return m_configuration.m_proxy;
1469}
1470
1471/*!
1472 \internal
1473 */
1474void QWebSocketPrivate::setProxy(const QNetworkProxy &networkProxy)
1475{
1476 if (m_configuration.m_proxy != networkProxy)
1477 m_configuration.m_proxy = networkProxy;
1478}
1479#endif //QT_NO_NETWORKPROXY
1480
1481/*!
1482 \internal
1483 */
1484void QWebSocketPrivate::setMaskGenerator(const QMaskGenerator *maskGenerator)
1485{
1486 if (!maskGenerator)
1487 m_pMaskGenerator = &m_defaultMaskGenerator;
1488 else if (maskGenerator != m_pMaskGenerator)
1489 m_pMaskGenerator = const_cast<QMaskGenerator *>(maskGenerator);
1490}
1491
1492/*!
1493 \internal
1494 */
1495const QMaskGenerator *QWebSocketPrivate::maskGenerator() const
1496{
1497 Q_ASSERT(m_pMaskGenerator);
1498 return m_pMaskGenerator;
1499}
1500
1501/*!
1502 \internal
1503 */
1504qint64 QWebSocketPrivate::readBufferSize() const
1505{
1506 return m_readBufferSize;
1507}
1508
1509/*!
1510 \internal
1511 */
1512void QWebSocketPrivate::resume()
1513{
1514 if (Q_LIKELY(m_pSocket))
1515 m_pSocket->resume();
1516}
1517
1518/*!
1519 \internal
1520 */
1521void QWebSocketPrivate::setPauseMode(QAbstractSocket::PauseModes pauseMode)
1522{
1523 m_pauseMode = pauseMode;
1524 if (Q_LIKELY(m_pSocket))
1525 m_pSocket->setPauseMode(m_pauseMode);
1526}
1527
1528/*!
1529 \internal
1530 */
1531void QWebSocketPrivate::setReadBufferSize(qint64 size)
1532{
1533 m_readBufferSize = size;
1534 if (Q_LIKELY(m_pSocket))
1535 m_pSocket->setReadBufferSize(m_readBufferSize);
1536}
1537
1538#ifndef Q_OS_WASM
1539/*!
1540 \internal
1541 */
1542bool QWebSocketPrivate::isValid() const
1543{
1544 return (m_pSocket && m_pSocket->isValid() &&
1545 (m_socketState == QAbstractSocket::ConnectedState));
1546}
1547#endif
1548
1549QT_END_NAMESPACE
1550

source code of qtwebsockets/src/websockets/qwebsocket_p.cpp