1 /****************************************************************************
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtWebSockets module of the Qt Toolkit.
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "qwebsocket.h"
43 #include "qwebsocket_p.h"
44 #include "qwebsocketprotocol_p.h"
45 #include "qwebsockethandshakerequest_p.h"
46 #include "qwebsockethandshakeresponse_p.h"
48 #include <QtCore/QUrl>
49 #include <QtNetwork/QAuthenticator>
50 #include <QtNetwork/QTcpSocket>
51 #include <QtCore/QByteArray>
52 #include <QtCore/QtEndian>
53 #include <QtCore/QCryptographicHash>
54 #include <QtCore/QRegularExpression>
55 #include <QtCore/QStringList>
56 #include <QtNetwork/QHostAddress>
57 #include <QtCore/QStringBuilder> //for more efficient string concatenation
58 #ifndef QT_NONETWORKPROXY
59 #include <QtNetwork/QNetworkProxy>
62 #include <QtNetwork/QSslConfiguration>
63 #include <QtNetwork/QSslError>
66 #include <QtCore/QDebug>
72 const quint64 FRAME_SIZE_IN_BYTES = 512 * 512 * 2; //maximum size of a frame when sending a message
74 QWebSocketConfiguration::QWebSocketConfiguration() :
76 m_sslConfiguration(QSslConfiguration::defaultConfiguration()),
78 m_ignoreSslErrors(false),
80 #ifndef QT_NONETWORKPROXY
81 m_proxy(QNetworkProxy::DefaultProxy)
89 QWebSocketPrivate::QWebSocketPrivate(const QString &origin, QWebSocketProtocol::Version version,
90 QWebSocket *pWebSocket, QObject *parent) :
101 m_socketState(QAbstractSocket::UnconnectedState),
104 m_isClosingHandshakeSent(false),
105 m_isClosingHandshakeReceived(false),
106 m_closeCode(QWebSocketProtocol::CC_NORMAL),
118 QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version,
119 QWebSocket *pWebSocket, QObject *parent) :
122 m_pSocket(pTcpSocket),
123 m_errorString(pTcpSocket->errorString()),
130 m_socketState(pTcpSocket->state()),
133 m_isClosingHandshakeSent(false),
134 m_isClosingHandshakeReceived(false),
135 m_closeCode(QWebSocketProtocol::CC_NORMAL),
142 makeConnections(m_pSocket.data());
148 void QWebSocketPrivate::init()
151 qsrand(static_cast<uint>(QDateTime::currentMSecsSinceEpoch()));
157 QWebSocketPrivate::~QWebSocketPrivate()
161 if (state() == QAbstractSocket::ConnectedState)
162 close(QWebSocketProtocol::CC_GOING_AWAY, tr("Connection closed"));
163 releaseConnections(m_pSocket.data());
169 void QWebSocketPrivate::abort()
178 QAbstractSocket::SocketError QWebSocketPrivate::error() const
180 QAbstractSocket::SocketError err = QAbstractSocket::OperationError;
181 if (Q_LIKELY(m_pSocket))
182 err = m_pSocket->error();
189 QString QWebSocketPrivate::errorString() const
192 if (!m_errorString.isEmpty())
193 errMsg = m_errorString;
195 errMsg = m_pSocket->errorString();
202 bool QWebSocketPrivate::flush()
205 if (Q_LIKELY(m_pSocket))
206 result = m_pSocket->flush();
213 qint64 QWebSocketPrivate::write(const char *message)
215 if (!message || !*message)
217 uint size = qstrlen(message);
218 qint64 maxSize = qMin(qint64(size), qint64(std::numeric_limits<QString::size_type>::max()));
219 return doWriteFrames(QString::fromUtf8(message, maxSize).toUtf8(), false);
225 qint64 QWebSocketPrivate::write(const char *message, qint64 maxSize)
227 if (!message || (maxSize <= qint64(0)) || !*message)
229 maxSize = qMin(maxSize, qint64(std::numeric_limits<QString::size_type>::max()));
230 return doWriteFrames(QString::fromUtf8(message, maxSize).toUtf8(), false);
236 qint64 QWebSocketPrivate::write(const QString &message)
238 return doWriteFrames(message.toUtf8(), false);
244 qint64 QWebSocketPrivate::write(const QByteArray &data)
246 return doWriteFrames(data, true);
253 void QWebSocketPrivate::setSslConfiguration(const QSslConfiguration &sslConfiguration)
255 m_configuration.m_sslConfiguration = sslConfiguration;
261 QSslConfiguration QWebSocketPrivate::sslConfiguration() const
263 return m_configuration.m_sslConfiguration;
269 void QWebSocketPrivate::ignoreSslErrors(const QList<QSslError> &errors)
271 m_configuration.m_ignoredSslErrors = errors;
277 void QWebSocketPrivate::ignoreSslErrors()
279 m_configuration.m_ignoreSslErrors = true;
280 if (Q_LIKELY(m_pSocket)) {
281 QSslSocket *pSslSocket = qobject_cast<QSslSocket *>(m_pSocket.data());
282 if (Q_LIKELY(pSslSocket))
283 pSslSocket->ignoreSslErrors();
290 Called from QWebSocketServer
293 QWebSocket *QWebSocketPrivate::upgradeFrom(QTcpSocket *pTcpSocket,
294 const QWebSocketHandshakeRequest &request,
295 const QWebSocketHandshakeResponse &response,
298 QWebSocket *pWebSocket = new QWebSocket(pTcpSocket, response.acceptedVersion(), parent);
299 if (Q_LIKELY(pWebSocket)) {
300 pWebSocket->d_func()->setExtension(response.acceptedExtension());
301 pWebSocket->d_func()->setOrigin(request.origin());
302 pWebSocket->d_func()->setRequestUrl(request.requestUrl());
303 pWebSocket->d_func()->setProtocol(response.acceptedProtocol());
304 pWebSocket->d_func()->setResourceName(request.requestUrl().toString(QUrl::RemoveUserInfo));
305 //a server should not send masked frames
306 pWebSocket->d_func()->enableMasking(false);
315 void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString reason)
317 if (Q_UNLIKELY(!m_pSocket))
319 if (!m_isClosingHandshakeSent) {
321 const quint16 code = qToBigEndian<quint16>(closeCode);
323 payload.append(static_cast<const char *>(static_cast<const void *>(&code)), 2);
324 if (!reason.isEmpty())
325 payload.append(reason.toUtf8());
326 quint32 maskingKey = 0;
328 maskingKey = generateMaskingKey();
329 QWebSocketProtocol::mask(payload.data(), payload.size(), maskingKey);
331 QByteArray frame = getFrameHeader(QWebSocketProtocol::OC_CLOSE,
332 payload.size(), maskingKey, true);
333 frame.append(payload);
334 m_pSocket->write(frame);
337 m_isClosingHandshakeSent = true;
339 Q_EMIT q->aboutToClose();
347 void QWebSocketPrivate::open(const QUrl &url, bool mask)
349 //just delete the old socket for the moment;
350 //later, we can add more 'intelligent' handling by looking at the url
352 QTcpSocket *pTcpSocket = m_pSocket.take();
354 releaseConnections(pTcpSocket);
355 pTcpSocket->deleteLater();
358 if (Q_LIKELY(!m_pSocket)) {
361 m_dataProcessor.clear();
362 m_isClosingHandshakeReceived = false;
363 m_isClosingHandshakeSent = false;
366 QString resourceName = url.path();
367 if (!url.query().isEmpty()) {
368 if (!resourceName.endsWith(QChar::fromLatin1('?'))) {
369 resourceName.append(QChar::fromLatin1('?'));
371 resourceName.append(url.query());
373 if (resourceName.isEmpty())
374 resourceName = QStringLiteral("/");
375 setResourceName(resourceName);
379 if (url.scheme() == QStringLiteral("wss")) {
380 if (!QSslSocket::supportsSsl()) {
381 const QString message = tr("SSL Sockets are not supported on this platform.");
382 setErrorString(message);
383 Q_EMIT q->error(QAbstractSocket::UnsupportedSocketOperationError);
385 QSslSocket *sslSocket = new QSslSocket(this);
386 m_pSocket.reset(sslSocket);
387 if (Q_LIKELY(m_pSocket)) {
388 m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
389 m_pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
391 makeConnections(m_pSocket.data());
392 connect(sslSocket, &QSslSocket::encryptedBytesWritten, q,
393 &QWebSocket::bytesWritten);
394 setSocketState(QAbstractSocket::ConnectingState);
396 sslSocket->setSslConfiguration(m_configuration.m_sslConfiguration);
397 if (Q_UNLIKELY(m_configuration.m_ignoreSslErrors))
398 sslSocket->ignoreSslErrors();
400 sslSocket->ignoreSslErrors(m_configuration.m_ignoredSslErrors);
401 #ifndef QT_NO_NETWORKPROXY
402 sslSocket->setProxy(m_configuration.m_proxy);
404 sslSocket->connectToHostEncrypted(url.host(), url.port(443));
406 const QString message = tr("Out of memory.");
407 setErrorString(message);
408 Q_EMIT q->error(QAbstractSocket::SocketResourceError);
413 if (url.scheme() == QStringLiteral("ws")) {
414 m_pSocket.reset(new QTcpSocket(this));
415 if (Q_LIKELY(m_pSocket)) {
416 m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
417 m_pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
419 makeConnections(m_pSocket.data());
420 connect(m_pSocket.data(), &QAbstractSocket::bytesWritten, q,
421 &QWebSocket::bytesWritten);
422 setSocketState(QAbstractSocket::ConnectingState);
423 #ifndef QT_NO_NETWORKPROXY
424 m_pSocket->setProxy(m_configuration.m_proxy);
426 m_pSocket->connectToHost(url.host(), url.port(80));
428 const QString message = tr("Out of memory.");
429 setErrorString(message);
430 Q_EMIT q->error(QAbstractSocket::SocketResourceError);
433 const QString message = tr("Unsupported websockets scheme: %1").arg(url.scheme());
434 setErrorString(message);
435 Q_EMIT q->error(QAbstractSocket::UnsupportedSocketOperationError);
443 void QWebSocketPrivate::ping(const QByteArray &payload)
445 QByteArray payloadTruncated = payload.left(125);
446 m_pingTimer.restart();
447 QByteArray pingFrame = getFrameHeader(QWebSocketProtocol::OC_PING, payloadTruncated.size(),
448 0 /*do not mask*/, true);
449 pingFrame.append(payloadTruncated);
450 qint64 ret = writeFrame(pingFrame);
456 Sets the version to use for the websocket protocol;
457 this must be set before the socket is opened.
459 void QWebSocketPrivate::setVersion(QWebSocketProtocol::Version version)
461 if (m_version != version)
467 Sets the resource name of the connection; must be set before the socket is openend
469 void QWebSocketPrivate::setResourceName(const QString &resourceName)
471 if (m_resourceName != resourceName)
472 m_resourceName = resourceName;
478 void QWebSocketPrivate::setRequestUrl(const QUrl &requestUrl)
480 if (m_requestUrl != requestUrl)
481 m_requestUrl = requestUrl;
487 void QWebSocketPrivate::setOrigin(const QString &origin)
489 if (m_origin != origin)
496 void QWebSocketPrivate::setProtocol(const QString &protocol)
498 if (m_protocol != protocol)
499 m_protocol = protocol;
505 void QWebSocketPrivate::setExtension(const QString &extension)
507 if (m_extension != extension)
508 m_extension = extension;
514 void QWebSocketPrivate::enableMasking(bool enable)
516 if (m_mustMask != enable)
523 void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket)
525 Q_ASSERT(pTcpSocket);
528 if (Q_LIKELY(pTcpSocket)) {
529 //pass through signals
530 typedef void (QAbstractSocket:: *ASErrorSignal)(QAbstractSocket::SocketError);
531 typedef void (QWebSocket:: *WSErrorSignal)(QAbstractSocket::SocketError);
533 static_cast<ASErrorSignal>(&QAbstractSocket::error),
534 q, static_cast<WSErrorSignal>(&QWebSocket::error));
535 connect(pTcpSocket, &QAbstractSocket::proxyAuthenticationRequired, q,
536 &QWebSocket::proxyAuthenticationRequired);
537 connect(pTcpSocket, &QAbstractSocket::readChannelFinished, q,
538 &QWebSocket::readChannelFinished);
539 connect(pTcpSocket, &QAbstractSocket::aboutToClose, q, &QWebSocket::aboutToClose);
542 connect(pTcpSocket, &QAbstractSocket::stateChanged, this,
543 &QWebSocketPrivate::processStateChanged);
544 //!!!important to use a QueuedConnection here;
545 //with QTcpSocket there is no problem, but with QSslSocket the processing hangs
546 connect(pTcpSocket, &QAbstractSocket::readyRead, this,
547 &QWebSocketPrivate::processData, Qt::QueuedConnection);
550 connect(&m_dataProcessor, &QWebSocketDataProcessor::textFrameReceived, q,
551 &QWebSocket::textFrameReceived);
552 connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryFrameReceived, q,
553 &QWebSocket::binaryFrameReceived);
554 connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryMessageReceived, q,
555 &QWebSocket::binaryMessageReceived);
556 connect(&m_dataProcessor, &QWebSocketDataProcessor::textMessageReceived, q,
557 &QWebSocket::textMessageReceived);
558 connect(&m_dataProcessor, &QWebSocketDataProcessor::errorEncountered, this,
559 &QWebSocketPrivate::close);
560 connect(&m_dataProcessor, &QWebSocketDataProcessor::pingReceived, this,
561 &QWebSocketPrivate::processPing);
562 connect(&m_dataProcessor, &QWebSocketDataProcessor::pongReceived, this,
563 &QWebSocketPrivate::processPong);
564 connect(&m_dataProcessor, &QWebSocketDataProcessor::closeReceived, this,
565 &QWebSocketPrivate::processClose);
571 void QWebSocketPrivate::releaseConnections(const QTcpSocket *pTcpSocket)
573 if (Q_LIKELY(pTcpSocket))
574 disconnect(pTcpSocket);
575 disconnect(&m_dataProcessor);
581 QWebSocketProtocol::Version QWebSocketPrivate::version() const
589 QString QWebSocketPrivate::resourceName() const
591 return m_resourceName;
597 QUrl QWebSocketPrivate::requestUrl() const
605 QString QWebSocketPrivate::origin() const
613 QString QWebSocketPrivate::protocol() const
621 QString QWebSocketPrivate::extension() const
629 QWebSocketProtocol::CloseCode QWebSocketPrivate::closeCode() const
637 QString QWebSocketPrivate::closeReason() const
639 return m_closeReason;
645 QByteArray QWebSocketPrivate::getFrameHeader(QWebSocketProtocol::OpCode opCode,
646 quint64 payloadLength, quint32 maskingKey,
651 bool ok = payloadLength <= 0x7FFFFFFFFFFFFFFFULL;
654 //FIN, RSV1-3, opcode
655 byte = static_cast<quint8>((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00)); //FIN, opcode
656 //RSV-1, RSV-2 and RSV-3 are zero
657 header.append(static_cast<char>(byte));
662 if (payloadLength <= 125) {
663 byte |= static_cast<quint8>(payloadLength);
664 header.append(static_cast<char>(byte));
665 } else if (payloadLength <= 0xFFFFU) {
667 header.append(static_cast<char>(byte));
668 quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
669 header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
670 } else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL) {
672 header.append(static_cast<char>(byte));
673 quint64 swapped = qToBigEndian<quint64>(payloadLength);
674 header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8);
677 if (maskingKey != 0) {
678 const quint32 mask = qToBigEndian<quint32>(maskingKey);
679 header.append(static_cast<const char *>(static_cast<const void *>(&mask)),
683 setErrorString(QStringLiteral("WebSocket::getHeader: payload too big!"));
684 Q_EMIT q_ptr->error(QAbstractSocket::DatagramTooLargeError);
693 qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary)
695 qint64 payloadWritten = 0;
696 if (Q_UNLIKELY(!m_pSocket))
697 return payloadWritten;
700 const QWebSocketProtocol::OpCode firstOpCode = isBinary ?
701 QWebSocketProtocol::OC_BINARY : QWebSocketProtocol::OC_TEXT;
703 int numFrames = data.size() / FRAME_SIZE_IN_BYTES;
704 QByteArray tmpData(data);
706 char *payload = tmpData.data();
707 quint64 sizeLeft = quint64(data.size()) % FRAME_SIZE_IN_BYTES;
708 if (Q_LIKELY(sizeLeft))
711 //catch the case where the payload is zero bytes;
712 //in this case, we still need to send a frame
713 if (Q_UNLIKELY(numFrames == 0))
715 quint64 currentPosition = 0;
716 qint64 bytesWritten = 0;
717 quint64 bytesLeft = data.size();
719 for (int i = 0; i < numFrames; ++i) {
720 quint32 maskingKey = 0;
722 maskingKey = generateMaskingKey();
724 const bool isLastFrame = (i == (numFrames - 1));
725 const bool isFirstFrame = (i == 0);
727 const quint64 size = qMin(bytesLeft, FRAME_SIZE_IN_BYTES);
728 const QWebSocketProtocol::OpCode opcode = isFirstFrame ? firstOpCode
729 : QWebSocketProtocol::OC_CONTINUE;
732 bytesWritten += m_pSocket->write(getFrameHeader(opcode, size, maskingKey, isLastFrame));
735 if (Q_LIKELY(size > 0)) {
736 char *currentData = payload + currentPosition;
738 QWebSocketProtocol::mask(currentData, size, maskingKey);
739 qint64 written = m_pSocket->write(currentData, static_cast<qint64>(size));
740 if (Q_LIKELY(written > 0)) {
741 bytesWritten += written;
742 payloadWritten += written;
745 setErrorString(tr("Error writing bytes to socket: %1.")
746 .arg(m_pSocket->errorString()));
747 Q_EMIT q->error(QAbstractSocket::NetworkError);
751 currentPosition += size;
754 if (Q_UNLIKELY(payloadWritten != data.size())) {
755 setErrorString(tr("Bytes written %1 != %2.").arg(payloadWritten).arg(data.size()));
756 Q_EMIT q->error(QAbstractSocket::NetworkError);
758 return payloadWritten;
764 quint32 QWebSocketPrivate::generateRandomNumber() const
766 return quint32((double(qrand()) / RAND_MAX) * std::numeric_limits<quint32>::max());
772 quint32 QWebSocketPrivate::generateMaskingKey() const
774 return generateRandomNumber();
780 QByteArray QWebSocketPrivate::generateKey() const
784 for (int i = 0; i < 4; ++i) {
785 const quint32 tmp = generateRandomNumber();
786 key.append(static_cast<const char *>(static_cast<const void *>(&tmp)), sizeof(quint32));
789 return key.toBase64();
796 QString QWebSocketPrivate::calculateAcceptKey(const QByteArray &key) const
798 const QByteArray tmpKey = key + QByteArrayLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
799 const QByteArray hash = QCryptographicHash::hash(tmpKey, QCryptographicHash::Sha1).toBase64();
800 return QString::fromLatin1(hash);
806 qint64 QWebSocketPrivate::writeFrames(const QList<QByteArray> &frames)
809 if (Q_LIKELY(m_pSocket)) {
810 QList<QByteArray>::const_iterator it;
811 for (it = frames.cbegin(); it < frames.cend(); ++it)
812 written += writeFrame(*it);
820 qint64 QWebSocketPrivate::writeFrame(const QByteArray &frame)
823 if (Q_LIKELY(m_pSocket))
824 written = m_pSocket->write(frame);
831 QString readLine(QTcpSocket *pSocket)
836 while (pSocket->getChar(&c)) {
837 if (c == char('\r')) {
838 pSocket->getChar(&c);
841 line.append(QChar::fromLatin1(c));
847 //called on the client for a server handshake response
851 void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket)
854 if (Q_UNLIKELY(!pSocket))
858 QString errorDescription;
860 const QString regExpStatusLine(QStringLiteral("^(HTTP/[0-9]+\\.[0-9]+)\\s([0-9]+)\\s(.*)"));
861 const QRegularExpression regExp(regExpStatusLine);
862 const QString statusLine = readLine(pSocket);
863 QString httpProtocol;
865 QString httpStatusMessage;
866 const QRegularExpressionMatch match = regExp.match(statusLine);
867 if (Q_LIKELY(match.hasMatch())) {
868 QStringList tokens = match.capturedTexts();
869 tokens.removeFirst(); //remove the search string
870 if (tokens.length() == 3) {
871 httpProtocol = tokens[0];
872 httpStatusCode = tokens[1].toInt();
873 httpStatusMessage = tokens[2].trimmed();
877 if (Q_UNLIKELY(!ok)) {
878 errorDescription = tr("Invalid statusline in response: %1.").arg(statusLine);
880 QString headerLine = readLine(pSocket);
881 QMap<QString, QString> headers;
882 while (!headerLine.isEmpty()) {
883 const QStringList headerField = headerLine.split(QStringLiteral(": "),
884 QString::SkipEmptyParts);
885 headers.insertMulti(headerField[0], headerField[1]);
886 headerLine = readLine(pSocket);
889 const QString acceptKey = headers.value(QStringLiteral("Sec-WebSocket-Accept"),
891 const QString upgrade = headers.value(QStringLiteral("Upgrade"), QString());
892 const QString connection = headers.value(QStringLiteral("Connection"), QString());
893 // unused for the moment
894 // const QString extensions = headers.value(QStringLiteral("Sec-WebSocket-Extensions"),
896 // const QString protocol = headers.value(QStringLiteral("Sec-WebSocket-Protocol"),
898 const QString version = headers.value(QStringLiteral("Sec-WebSocket-Version"),
901 if (Q_LIKELY(httpStatusCode == 101)) {
902 //HTTP/x.y 101 Switching Protocols
903 bool conversionOk = false;
904 const float version = httpProtocol.midRef(5).toFloat(&conversionOk);
905 //TODO: do not check the httpStatusText right now
906 ok = !(acceptKey.isEmpty() ||
907 (!conversionOk || (version < 1.1f)) ||
908 (upgrade.toLower() != QStringLiteral("websocket")) ||
909 (connection.toLower() != QStringLiteral("upgrade")));
911 const QString accept = calculateAcceptKey(m_key);
912 ok = (accept == acceptKey);
915 tr("Accept-Key received from server %1 does not match the client key %2.")
916 .arg(acceptKey).arg(accept);
919 tr("QWebSocketPrivate::processHandshake: Invalid statusline in response: %1.")
922 } else if (httpStatusCode == 400) {
923 //HTTP/1.1 400 Bad Request
924 if (!version.isEmpty()) {
925 const QStringList versions = version.split(QStringLiteral(", "),
926 QString::SkipEmptyParts);
927 if (!versions.contains(QString::number(QWebSocketProtocol::currentVersion()))) {
928 //if needed to switch protocol version, then we are finished here
929 //because we cannot handle other protocols than the RFC one (v13)
931 tr("Handshake: Server requests a version that we don't support: %1.")
932 .arg(versions.join(QStringLiteral(", ")));
935 //we tried v13, but something different went wrong
937 tr("QWebSocketPrivate::processHandshake: Unknown error condition " \
938 "encountered. Aborting connection.");
944 tr("QWebSocketPrivate::processHandshake: Unhandled http status code: %1 (%2).")
945 .arg(httpStatusCode).arg(httpStatusMessage);
950 setErrorString(errorDescription);
951 Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
953 //handshake succeeded
954 setSocketState(QAbstractSocket::ConnectedState);
955 Q_EMIT q->connected();
963 void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketState)
967 QAbstractSocket::SocketState webSocketState = this->state();
968 switch (socketState) {
969 case QAbstractSocket::ConnectedState:
970 if (webSocketState == QAbstractSocket::ConnectingState) {
971 m_key = generateKey();
972 const QString handshake =
973 createHandShakeRequest(m_resourceName,
975 % QStringLiteral(":")
976 % QString::number(m_requestUrl.port(80)),
981 m_pSocket->write(handshake.toLatin1());
985 case QAbstractSocket::ClosingState:
986 if (webSocketState == QAbstractSocket::ConnectedState)
987 setSocketState(QAbstractSocket::ClosingState);
990 case QAbstractSocket::UnconnectedState:
991 if (webSocketState != QAbstractSocket::UnconnectedState) {
992 setSocketState(QAbstractSocket::UnconnectedState);
993 Q_EMIT q->disconnected();
997 case QAbstractSocket::HostLookupState:
998 case QAbstractSocket::ConnectingState:
999 case QAbstractSocket::BoundState:
1000 case QAbstractSocket::ListeningState:
1002 //to make C++ compiler happy;
1012 void QWebSocketPrivate::processData()
1014 Q_ASSERT(m_pSocket);
1015 while (m_pSocket->bytesAvailable()) {
1016 if (state() == QAbstractSocket::ConnectingState)
1017 processHandshake(m_pSocket.data());
1019 m_dataProcessor.process(m_pSocket.data());
1026 void QWebSocketPrivate::processPing(const QByteArray &data)
1028 Q_ASSERT(m_pSocket);
1029 quint32 maskingKey = 0;
1031 maskingKey = generateMaskingKey();
1032 m_pSocket->write(getFrameHeader(QWebSocketProtocol::OC_PONG, data.size(), maskingKey, true));
1033 if (data.size() > 0) {
1034 QByteArray maskedData = data;
1036 QWebSocketProtocol::mask(&maskedData, maskingKey);
1037 m_pSocket->write(maskedData);
1044 void QWebSocketPrivate::processPong(const QByteArray &data)
1047 Q_EMIT q->pong(static_cast<quint64>(m_pingTimer.elapsed()), data);
1053 void QWebSocketPrivate::processClose(QWebSocketProtocol::CloseCode closeCode, QString closeReason)
1055 m_isClosingHandshakeReceived = true;
1056 close(closeCode, closeReason);
1062 QString QWebSocketPrivate::createHandShakeRequest(QString resourceName,
1069 QStringList handshakeRequest;
1071 handshakeRequest << QStringLiteral("GET ") % resourceName % QStringLiteral(" HTTP/1.1") <<
1072 QStringLiteral("Host: ") % host <<
1073 QStringLiteral("Upgrade: websocket") <<
1074 QStringLiteral("Connection: Upgrade") <<
1075 QStringLiteral("Sec-WebSocket-Key: ") % QString::fromLatin1(key);
1076 if (!origin.isEmpty())
1077 handshakeRequest << QStringLiteral("Origin: ") % origin;
1078 handshakeRequest << QStringLiteral("Sec-WebSocket-Version: ")
1079 % QString::number(QWebSocketProtocol::currentVersion());
1080 if (extensions.length() > 0)
1081 handshakeRequest << QStringLiteral("Sec-WebSocket-Extensions: ") % extensions;
1082 if (protocols.length() > 0)
1083 handshakeRequest << QStringLiteral("Sec-WebSocket-Protocol: ") % protocols;
1084 handshakeRequest << QStringLiteral("\r\n");
1086 return handshakeRequest.join(QStringLiteral("\r\n"));
1092 QAbstractSocket::SocketState QWebSocketPrivate::state() const
1094 return m_socketState;
1100 void QWebSocketPrivate::setSocketState(QAbstractSocket::SocketState state)
1103 if (m_socketState != state) {
1104 m_socketState = state;
1105 Q_EMIT q->stateChanged(m_socketState);
1112 void QWebSocketPrivate::setErrorString(const QString &errorString)
1114 if (m_errorString != errorString)
1115 m_errorString = errorString;
1121 QHostAddress QWebSocketPrivate::localAddress() const
1123 QHostAddress address;
1124 if (Q_LIKELY(m_pSocket))
1125 address = m_pSocket->localAddress();
1132 quint16 QWebSocketPrivate::localPort() const
1135 if (Q_LIKELY(m_pSocket))
1136 port = m_pSocket->localPort();
1143 QAbstractSocket::PauseModes QWebSocketPrivate::pauseMode() const
1145 QAbstractSocket::PauseModes mode = QAbstractSocket::PauseNever;
1146 if (Q_LIKELY(m_pSocket))
1147 mode = m_pSocket->pauseMode();
1154 QHostAddress QWebSocketPrivate::peerAddress() const
1156 QHostAddress address;
1157 if (Q_LIKELY(m_pSocket))
1158 address = m_pSocket->peerAddress();
1165 QString QWebSocketPrivate::peerName() const
1168 if (Q_LIKELY(m_pSocket))
1169 name = m_pSocket->peerName();
1176 quint16 QWebSocketPrivate::peerPort() const
1179 if (Q_LIKELY(m_pSocket))
1180 port = m_pSocket->peerPort();
1184 #ifndef QT_NO_NETWORKPROXY
1188 QNetworkProxy QWebSocketPrivate::proxy() const
1190 return m_configuration.m_proxy;
1196 void QWebSocketPrivate::setProxy(const QNetworkProxy &networkProxy)
1198 if (networkProxy != networkProxy)
1199 m_configuration.m_proxy = networkProxy;
1201 #endif //QT_NO_NETWORKPROXY
1206 qint64 QWebSocketPrivate::readBufferSize() const
1209 if (Q_LIKELY(m_pSocket))
1210 size = m_pSocket->readBufferSize();
1217 void QWebSocketPrivate::resume()
1219 if (Q_LIKELY(m_pSocket))
1220 m_pSocket->resume();
1226 void QWebSocketPrivate::setPauseMode(QAbstractSocket::PauseModes pauseMode)
1228 if (Q_LIKELY(m_pSocket))
1229 m_pSocket->setPauseMode(pauseMode);
1235 void QWebSocketPrivate::setReadBufferSize(qint64 size)
1237 if (Q_LIKELY(m_pSocket))
1238 m_pSocket->setReadBufferSize(size);
1244 void QWebSocketPrivate::setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value)
1246 if (Q_LIKELY(m_pSocket))
1247 m_pSocket->setSocketOption(option, value);
1253 QVariant QWebSocketPrivate::socketOption(QAbstractSocket::SocketOption option)
1256 if (Q_LIKELY(m_pSocket))
1257 val = m_pSocket->socketOption(option);
1264 bool QWebSocketPrivate::isValid() const
1266 return (m_pSocket && m_pSocket->isValid());