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