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/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>
61 #include <QtNetwork/QSslConfiguration>
62 #include <QtNetwork/QSslError>
65 #include <QtCore/QDebug>
71 const quint64 FRAME_SIZE_IN_BYTES = 512 * 512 * 2; //maximum size of a frame when sending a message
73 QWebSocketConfiguration::QWebSocketConfiguration() :
75 m_sslConfiguration(QSslConfiguration::defaultConfiguration()),
77 m_ignoreSslErrors(false),
79 #ifndef QT_NONETWORKPROXY
80 m_proxy(QNetworkProxy::DefaultProxy)
88 QWebSocketPrivate::QWebSocketPrivate(const QString &origin, QWebSocketProtocol::Version version, QWebSocket *pWebSocket, QObject *parent) :
99 m_socketState(QAbstractSocket::UnconnectedState),
102 m_isClosingHandshakeSent(false),
103 m_isClosingHandshakeReceived(false),
104 m_closeCode(QWebSocketProtocol::CC_NORMAL),
110 Q_ASSERT(pWebSocket);
111 qsrand(static_cast<uint>(QDateTime::currentMSecsSinceEpoch()));
117 QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version, QWebSocket *pWebSocket, QObject *parent) :
120 m_pSocket(pTcpSocket),
121 m_errorString(pTcpSocket->errorString()),
128 m_socketState(pTcpSocket->state()),
131 m_isClosingHandshakeSent(false),
132 m_isClosingHandshakeReceived(false),
133 m_closeCode(QWebSocketProtocol::CC_NORMAL),
139 Q_ASSERT(pWebSocket);
140 makeConnections(m_pSocket);
146 QWebSocketPrivate::~QWebSocketPrivate()
150 if (state() == QAbstractSocket::ConnectedState)
152 close(QWebSocketProtocol::CC_GOING_AWAY, tr("Connection closed"));
154 releaseConnections(m_pSocket);
155 m_pSocket->deleteLater();
156 m_pSocket = Q_NULLPTR;
163 void QWebSocketPrivate::abort()
174 QAbstractSocket::SocketError QWebSocketPrivate::error() const
176 QAbstractSocket::SocketError err = QAbstractSocket::OperationError;
179 err = m_pSocket->error();
187 QString QWebSocketPrivate::errorString() const
190 if (!m_errorString.isEmpty())
192 errMsg = m_errorString;
196 errMsg = m_pSocket->errorString();
204 bool QWebSocketPrivate::flush()
209 result = m_pSocket->flush();
217 qint64 QWebSocketPrivate::write(const char *message)
219 //TODO: create a QByteArray from message, and directly call doWriteData
220 //now the data is converted to a string, and then converted back to a bytearray
221 return write(QString::fromUtf8(message));
227 qint64 QWebSocketPrivate::write(const char *message, qint64 maxSize)
229 //TODO: create a QByteArray from message, and directly call doWriteData
230 //now the data is converted to a string, and then converted back to a bytearray
231 return write(QString::fromUtf8(message, static_cast<int>(maxSize)));
237 qint64 QWebSocketPrivate::write(const QString &message)
239 return doWriteData(message.toUtf8(), false);
245 qint64 QWebSocketPrivate::write(const QByteArray &data)
247 return doWriteData(data, true);
254 void QWebSocketPrivate::setSslConfiguration(const QSslConfiguration &sslConfiguration)
256 m_configuration.m_sslConfiguration = sslConfiguration;
262 QSslConfiguration QWebSocketPrivate::sslConfiguration() const
264 return m_configuration.m_sslConfiguration;
270 void QWebSocketPrivate::ignoreSslErrors(const QList<QSslError> &errors)
272 m_configuration.m_ignoredSslErrors = errors;
278 void QWebSocketPrivate::ignoreSslErrors()
280 m_configuration.m_ignoreSslErrors = true;
283 QSslSocket *pSslSocket = qobject_cast<QSslSocket *>(m_pSocket);
286 pSslSocket->ignoreSslErrors();
294 Called from QWebSocketServer
297 QWebSocket *QWebSocketPrivate::upgradeFrom(QTcpSocket *pTcpSocket,
298 const QWebSocketHandshakeRequest &request,
299 const QWebSocketHandshakeResponse &response,
302 QWebSocket *pWebSocket = new QWebSocket(pTcpSocket, response.acceptedVersion(), parent);
303 pWebSocket->d_func()->setExtension(response.acceptedExtension());
304 pWebSocket->d_func()->setOrigin(request.origin());
305 pWebSocket->d_func()->setRequestUrl(request.requestUrl());
306 pWebSocket->d_func()->setProtocol(response.acceptedProtocol());
307 pWebSocket->d_func()->setResourceName(request.requestUrl().toString(QUrl::RemoveUserInfo));
308 //a server should not send masked frames
309 pWebSocket->d_func()->enableMasking(false);
317 void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString reason)
321 if (!m_isClosingHandshakeSent)
324 quint32 maskingKey = 0;
327 maskingKey = generateMaskingKey();
329 const quint16 code = qToBigEndian<quint16>(closeCode);
331 payload.append(static_cast<const char *>(static_cast<const void *>(&code)), 2);
332 if (!reason.isEmpty())
334 payload.append(reason.toUtf8());
338 QWebSocketProtocol::mask(payload.data(), payload.size(), maskingKey);
340 QByteArray frame = getFrameHeader(QWebSocketProtocol::OC_CLOSE, payload.size(), maskingKey, true);
341 frame.append(payload);
342 m_pSocket->write(frame);
345 m_isClosingHandshakeSent = true;
347 Q_EMIT q->aboutToClose();
356 void QWebSocketPrivate::open(const QUrl &url, bool mask)
362 m_dataProcessor.clear();
363 m_isClosingHandshakeReceived = false;
364 m_isClosingHandshakeSent = false;
367 QString resourceName = url.path();
368 if (!url.query().isEmpty())
370 if (!resourceName.endsWith(QChar::fromLatin1('?')))
372 resourceName.append(QChar::fromLatin1('?'));
374 resourceName.append(url.query());
376 if (resourceName.isEmpty())
378 resourceName = QStringLiteral("/");
380 setResourceName(resourceName);
384 if (url.scheme() == QStringLiteral("wss"))
386 if (!QSslSocket::supportsSsl())
388 const QString message = tr("SSL Sockets are not supported on this platform.");
389 qWarning() << message;
390 setErrorString(message);
391 emit q->error(QAbstractSocket::UnsupportedSocketOperationError);
395 QSslSocket *sslSocket = new QSslSocket(this);
396 m_pSocket = sslSocket;
398 makeConnections(m_pSocket);
399 connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)), q, SIGNAL(bytesWritten(qint64)));
400 setSocketState(QAbstractSocket::ConnectingState);
402 sslSocket->setSslConfiguration(m_configuration.m_sslConfiguration);
403 if (m_configuration.m_ignoreSslErrors)
405 sslSocket->ignoreSslErrors();
409 sslSocket->ignoreSslErrors(m_configuration.m_ignoredSslErrors);
411 #ifndef QT_NO_NETWORKPROXY
412 sslSocket->setProxy(m_configuration.m_proxy);
414 sslSocket->connectToHostEncrypted(url.host(), url.port(443));
419 if (url.scheme() == QStringLiteral("ws"))
421 m_pSocket = new QTcpSocket(this);
423 makeConnections(m_pSocket);
424 connect(m_pSocket, SIGNAL(bytesWritten(qint64)), q, SIGNAL(bytesWritten(qint64)));
425 setSocketState(QAbstractSocket::ConnectingState);
426 #ifndef QT_NO_NETWORKPROXY
427 m_pSocket->setProxy(m_configuration.m_proxy);
429 m_pSocket->connectToHost(url.host(), url.port(80));
433 const QString message = tr("Unsupported websockets scheme: %1").arg(url.scheme());
434 qWarning() << message;
435 setErrorString(message);
436 emit q->error(QAbstractSocket::UnsupportedSocketOperationError);
444 void QWebSocketPrivate::ping(const QByteArray &payload)
446 Q_ASSERT(payload.length() < 126);
447 m_pingTimer.restart();
448 QByteArray pingFrame = getFrameHeader(QWebSocketProtocol::OC_PING, payload.size(), 0 /*do not mask*/, true);
449 pingFrame.append(payload);
450 writeFrame(pingFrame);
455 Sets the version to use for the websocket protocol; this must be set before the socket is opened.
457 void QWebSocketPrivate::setVersion(QWebSocketProtocol::Version version)
464 Sets the resource name of the connection; must be set before the socket is openend
466 void QWebSocketPrivate::setResourceName(const QString &resourceName)
468 m_resourceName = resourceName;
474 void QWebSocketPrivate::setRequestUrl(const QUrl &requestUrl)
476 m_requestUrl = requestUrl;
482 void QWebSocketPrivate::setOrigin(const QString &origin)
490 void QWebSocketPrivate::setProtocol(const QString &protocol)
492 m_protocol = protocol;
498 void QWebSocketPrivate::setExtension(const QString &extension)
500 m_extension = extension;
506 void QWebSocketPrivate::enableMasking(bool enable)
514 qint64 QWebSocketPrivate::doWriteData(const QByteArray &data, bool isBinary)
516 return doWriteFrames(data, isBinary);
522 void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket)
524 Q_ASSERT(pTcpSocket);
527 //pass through signals
528 connect(pTcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), q, SIGNAL(error(QAbstractSocket::SocketError)));
529 connect(pTcpSocket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), q, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)));
530 connect(pTcpSocket, SIGNAL(readChannelFinished()), q, SIGNAL(readChannelFinished()));
531 connect(pTcpSocket, SIGNAL(aboutToClose()), q, SIGNAL(aboutToClose()));
532 //connect(pTcpSocket, SIGNAL(bytesWritten(qint64)), q, SIGNAL(bytesWritten(qint64)));
535 connect(pTcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(processStateChanged(QAbstractSocket::SocketState)));
536 //!!!important to use a QueuedConnection here; with QTcpSocket there is no problem, but with QSslSocket the processing hangs
537 connect(pTcpSocket, SIGNAL(readyRead()), this, SLOT(processData()), Qt::QueuedConnection);
539 connect(&m_dataProcessor, SIGNAL(textFrameReceived(QString,bool)), q, SIGNAL(textFrameReceived(QString,bool)));
540 connect(&m_dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)), q, SIGNAL(binaryFrameReceived(QByteArray,bool)));
541 connect(&m_dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)), q, SIGNAL(binaryMessageReceived(QByteArray)));
542 connect(&m_dataProcessor, SIGNAL(textMessageReceived(QString)), q, SIGNAL(textMessageReceived(QString)));
543 connect(&m_dataProcessor, SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)), this, SLOT(close(QWebSocketProtocol::CloseCode,QString)));
544 connect(&m_dataProcessor, SIGNAL(pingReceived(QByteArray)), this, SLOT(processPing(QByteArray)));
545 connect(&m_dataProcessor, SIGNAL(pongReceived(QByteArray)), this, SLOT(processPong(QByteArray)));
546 connect(&m_dataProcessor, SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString)), this, SLOT(processClose(QWebSocketProtocol::CloseCode,QString)));
552 void QWebSocketPrivate::releaseConnections(const QTcpSocket *pTcpSocket)
557 //pass through signals
558 disconnect(pTcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), q, SIGNAL(error(QAbstractSocket::SocketError)));
559 disconnect(pTcpSocket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), q, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)));
560 disconnect(pTcpSocket, SIGNAL(readChannelFinished()), q, SIGNAL(readChannelFinished()));
561 disconnect(pTcpSocket, SIGNAL(aboutToClose()), q, SIGNAL(aboutToClose()));
562 //disconnect(pTcpSocket, SIGNAL(bytesWritten(qint64)), q, SIGNAL(bytesWritten(qint64)));
565 disconnect(pTcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(processStateChanged(QAbstractSocket::SocketState)));
566 disconnect(pTcpSocket, SIGNAL(readyRead()), this, SLOT(processData()));
568 disconnect(&m_dataProcessor, SIGNAL(pingReceived(QByteArray)), this, SLOT(processPing(QByteArray)));
569 disconnect(&m_dataProcessor, SIGNAL(pongReceived(QByteArray)), this, SLOT(processPong(QByteArray)));
570 disconnect(&m_dataProcessor, SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString)), this, SLOT(processClose(QWebSocketProtocol::CloseCode,QString)));
571 disconnect(&m_dataProcessor, SIGNAL(textFrameReceived(QString,bool)), q, SIGNAL(textFrameReceived(QString,bool)));
572 disconnect(&m_dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)), q, SIGNAL(binaryFrameReceived(QByteArray,bool)));
573 disconnect(&m_dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)), q, SIGNAL(binaryMessageReceived(QByteArray)));
574 disconnect(&m_dataProcessor, SIGNAL(textMessageReceived(QString)), q, SIGNAL(textMessageReceived(QString)));
575 disconnect(&m_dataProcessor, SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)), this, SLOT(close(QWebSocketProtocol::CloseCode,QString)));
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, quint64 payloadLength, quint32 maskingKey, bool lastFrame) const
649 bool ok = payloadLength <= 0x7FFFFFFFFFFFFFFFULL;
653 //FIN, RSV1-3, opcode
654 byte = static_cast<quint8>((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00)); //FIN, opcode
655 //RSV-1, RSV-2 and RSV-3 are zero
656 header.append(static_cast<char>(byte));
658 //Now write the masking bit and the payload length byte
664 if (payloadLength <= 125)
666 byte |= static_cast<quint8>(payloadLength);
667 header.append(static_cast<char>(byte));
669 else if (payloadLength <= 0xFFFFU)
672 header.append(static_cast<char>(byte));
673 quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
674 header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
676 else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL)
679 header.append(static_cast<char>(byte));
680 quint64 swapped = qToBigEndian<quint64>(payloadLength);
681 header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8);
687 //TODO: to big endian?
688 const quint32 mask = qToBigEndian<quint32>(maskingKey);
689 header.append(static_cast<const char *>(static_cast<const void *>(&mask)), sizeof(quint32));
694 //setErrorString("WebSocket::getHeader: payload too big!");
695 //Q_EMIT q_ptr->error(QAbstractSocket::DatagramTooLargeError);
696 qDebug() << "WebSocket::getHeader: payload too big!";
705 qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary)
707 qint64 payloadWritten = 0;
711 const QWebSocketProtocol::OpCode firstOpCode = isBinary ? QWebSocketProtocol::OC_BINARY : QWebSocketProtocol::OC_TEXT;
713 int numFrames = data.size() / FRAME_SIZE_IN_BYTES;
714 QByteArray tmpData(data);
716 char *payload = tmpData.data();
717 quint64 sizeLeft = static_cast<quint64>(data.size()) % FRAME_SIZE_IN_BYTES;
722 if (numFrames == 0) //catch the case where the payload is zero bytes; in that case, we still need to send a frame
726 quint64 currentPosition = 0;
727 qint64 bytesWritten = 0;
728 quint64 bytesLeft = data.size();
730 for (int i = 0; i < numFrames; ++i)
732 quint32 maskingKey = 0;
735 maskingKey = generateMaskingKey();
738 const bool isLastFrame = (i == (numFrames - 1));
739 const bool isFirstFrame = (i == 0);
741 const quint64 size = qMin(bytesLeft, FRAME_SIZE_IN_BYTES);
742 const QWebSocketProtocol::OpCode opcode = isFirstFrame ? firstOpCode : QWebSocketProtocol::OC_CONTINUE;
745 bytesWritten += m_pSocket->write(getFrameHeader(opcode, size, maskingKey, isLastFrame));
750 char *currentData = payload + currentPosition;
753 QWebSocketProtocol::mask(currentData, size, maskingKey);
755 qint64 written = m_pSocket->write(currentData, static_cast<qint64>(size));
758 bytesWritten += written;
759 payloadWritten += written;
763 setErrorString(tr("Error writing bytes to socket: %1.").arg(m_pSocket->errorString()));
764 qDebug() << errorString();
766 Q_EMIT q->error(QAbstractSocket::NetworkError);
770 currentPosition += size;
773 if (payloadWritten != data.size())
775 setErrorString(tr("Bytes written %1 != %2.").arg(payloadWritten).arg(data.size()));
776 qDebug() << errorString();
777 Q_EMIT q->error(QAbstractSocket::NetworkError);
780 return payloadWritten;
786 quint32 QWebSocketPrivate::generateRandomNumber() const
788 return static_cast<quint32>((static_cast<double>(qrand()) / RAND_MAX) * std::numeric_limits<quint32>::max());
794 quint32 QWebSocketPrivate::generateMaskingKey() const
796 return generateRandomNumber();
802 QByteArray QWebSocketPrivate::generateKey() const
806 for (int i = 0; i < 4; ++i)
808 quint32 tmp = generateRandomNumber();
809 key.append(static_cast<const char *>(static_cast<const void *>(&tmp)), sizeof(quint32));
812 return key.toBase64();
819 QString QWebSocketPrivate::calculateAcceptKey(const QString &key) const
821 const QString tmpKey = key % QStringLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
822 const QByteArray hash = QCryptographicHash::hash(tmpKey.toLatin1(), QCryptographicHash::Sha1);
823 return QString::fromLatin1(hash.toBase64());
829 qint64 QWebSocketPrivate::writeFrames(const QList<QByteArray> &frames)
834 for (int i = 0; i < frames.size(); ++i)
836 written += writeFrame(frames[i]);
845 qint64 QWebSocketPrivate::writeFrame(const QByteArray &frame)
850 written = m_pSocket->write(frame);
858 QString readLine(QTcpSocket *pSocket)
865 while (pSocket->getChar(&c))
869 pSocket->getChar(&c);
874 line.append(QChar::fromLatin1(c));
881 //called on the client for a server handshake response
885 void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket)
894 QString errorDescription;
896 const QString regExpStatusLine(QStringLiteral("^(HTTP/[0-9]+\\.[0-9]+)\\s([0-9]+)\\s(.*)"));
897 const QRegularExpression regExp(regExpStatusLine);
898 const QString statusLine = readLine(pSocket);
899 QString httpProtocol;
901 QString httpStatusMessage;
902 const QRegularExpressionMatch match = regExp.match(statusLine);
903 if (match.hasMatch())
905 QStringList tokens = match.capturedTexts();
906 tokens.removeFirst(); //remove the search string
907 if (tokens.length() == 3)
909 httpProtocol = tokens[0];
910 httpStatusCode = tokens[1].toInt();
911 httpStatusMessage = tokens[2].trimmed();
917 errorDescription = tr("Invalid statusline in response: %1.").arg(statusLine);
921 QString headerLine = readLine(pSocket);
922 QMap<QString, QString> headers;
923 while (!headerLine.isEmpty())
925 const QStringList headerField = headerLine.split(QStringLiteral(": "), QString::SkipEmptyParts);
926 headers.insertMulti(headerField[0], headerField[1]);
927 headerLine = readLine(pSocket);
930 const QString acceptKey = headers.value(QStringLiteral("Sec-WebSocket-Accept"), QStringLiteral(""));
931 const QString upgrade = headers.value(QStringLiteral("Upgrade"), QStringLiteral(""));
932 const QString connection = headers.value(QStringLiteral("Connection"), QStringLiteral(""));
933 //unused for the moment
934 //const QString extensions = headers.value(QStringLiteral("Sec-WebSocket-Extensions"), QStringLiteral(""));
935 //const QString protocol = headers.value(QStringLiteral("Sec-WebSocket-Protocol"), QStringLiteral(""));
936 const QString version = headers.value(QStringLiteral("Sec-WebSocket-Version"), QStringLiteral(""));
938 if (httpStatusCode == 101) //HTTP/x.y 101 Switching Protocols
940 bool conversionOk = false;
941 const float version = httpProtocol.midRef(5).toFloat(&conversionOk);
942 //TODO: do not check the httpStatusText right now
943 ok = !(acceptKey.isEmpty() ||
944 (!conversionOk || (version < 1.1f)) ||
945 (upgrade.toLower() != QStringLiteral("websocket")) ||
946 (connection.toLower() != QStringLiteral("upgrade")));
949 const QString accept = calculateAcceptKey(QString::fromLatin1(m_key));
950 ok = (accept == acceptKey);
953 errorDescription = tr("Accept-Key received from server %1 does not match the client key %2.").arg(acceptKey).arg(accept);
958 errorDescription = tr("QWebSocketPrivate::processHandshake: Invalid statusline in response: %1.").arg(statusLine);
961 else if (httpStatusCode == 400) //HTTP/1.1 400 Bad Request
963 if (!version.isEmpty())
965 const QStringList versions = version.split(QStringLiteral(", "), QString::SkipEmptyParts);
966 if (!versions.contains(QString::number(QWebSocketProtocol::currentVersion())))
968 //if needed to switch protocol version, then we are finished here
969 //because we cannot handle other protocols than the RFC one (v13)
970 errorDescription = tr("Handshake: Server requests a version that we don't support: %1.").arg(versions.join(QStringLiteral(", ")));
975 //we tried v13, but something different went wrong
976 errorDescription = tr("QWebSocketPrivate::processHandshake: Unknown error condition encountered. Aborting connection.");
983 errorDescription = tr("QWebSocketPrivate::processHandshake: Unhandled http status code: %1 (%2).").arg(httpStatusCode).arg(httpStatusMessage);
989 qDebug() << errorDescription;
990 setErrorString(errorDescription);
991 Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
995 //handshake succeeded
996 setSocketState(QAbstractSocket::ConnectedState);
997 Q_EMIT q->connected();
1005 void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketState)
1007 Q_ASSERT(m_pSocket);
1009 QAbstractSocket::SocketState webSocketState = this->state();
1010 switch (socketState)
1012 case QAbstractSocket::ConnectedState:
1014 if (webSocketState == QAbstractSocket::ConnectingState)
1016 m_key = generateKey();
1017 QString handshake = createHandShakeRequest(m_resourceName, m_requestUrl.host() % QStringLiteral(":") % QString::number(m_requestUrl.port(80)), origin(), QStringLiteral(""), QStringLiteral(""), m_key);
1018 m_pSocket->write(handshake.toLatin1());
1022 case QAbstractSocket::ClosingState:
1024 if (webSocketState == QAbstractSocket::ConnectedState)
1026 setSocketState(QAbstractSocket::ClosingState);
1030 case QAbstractSocket::UnconnectedState:
1032 if (webSocketState != QAbstractSocket::UnconnectedState)
1034 setSocketState(QAbstractSocket::UnconnectedState);
1035 Q_EMIT q->disconnected();
1039 case QAbstractSocket::HostLookupState:
1040 case QAbstractSocket::ConnectingState:
1041 case QAbstractSocket::BoundState:
1042 case QAbstractSocket::ListeningState:
1045 //to make C++ compiler happy;
1056 //connectToHost is called
1057 //our socket state is set to "connecting", and tcpSocket->connectToHost is called
1058 //the tcpsocket is opened, a handshake message is sent; a readyRead signal is thrown
1059 //this signal is catched by processData
1060 //when OUR socket state is in the "connecting state", this means that
1061 //we have received data from the server (response to handshake), and that we
1062 //should "upgrade" our socket to a websocket (connected state)
1063 //if our socket was already upgraded, then we need to process websocket data
1067 void QWebSocketPrivate::processData()
1069 Q_ASSERT(m_pSocket);
1070 while (m_pSocket->bytesAvailable())
1072 if (state() == QAbstractSocket::ConnectingState)
1074 processHandshake(m_pSocket);
1078 m_dataProcessor.process(m_pSocket);
1086 void QWebSocketPrivate::processPing(QByteArray data)
1088 Q_ASSERT(m_pSocket);
1089 quint32 maskingKey = 0;
1092 maskingKey = generateMaskingKey();
1094 m_pSocket->write(getFrameHeader(QWebSocketProtocol::OC_PONG, data.size(), maskingKey, true));
1095 if (data.size() > 0)
1099 QWebSocketProtocol::mask(&data, maskingKey);
1101 m_pSocket->write(data);
1108 void QWebSocketPrivate::processPong(QByteArray data)
1111 Q_EMIT q->pong(static_cast<quint64>(m_pingTimer.elapsed()), data);
1117 void QWebSocketPrivate::processClose(QWebSocketProtocol::CloseCode closeCode, QString closeReason)
1119 m_isClosingHandshakeReceived = true;
1120 close(closeCode, closeReason);
1126 QString QWebSocketPrivate::createHandShakeRequest(QString resourceName,
1133 QStringList handshakeRequest;
1135 handshakeRequest << QStringLiteral("GET ") % resourceName % QStringLiteral(" HTTP/1.1") <<
1136 QStringLiteral("Host: ") % host <<
1137 QStringLiteral("Upgrade: websocket") <<
1138 QStringLiteral("Connection: Upgrade") <<
1139 QStringLiteral("Sec-WebSocket-Key: ") % QString::fromLatin1(key);
1140 if (!origin.isEmpty())
1142 handshakeRequest << QStringLiteral("Origin: ") % origin;
1144 handshakeRequest << QStringLiteral("Sec-WebSocket-Version: ") % QString::number(QWebSocketProtocol::currentVersion());
1145 if (extensions.length() > 0)
1147 handshakeRequest << QStringLiteral("Sec-WebSocket-Extensions: ") % extensions;
1149 if (protocols.length() > 0)
1151 handshakeRequest << QStringLiteral("Sec-WebSocket-Protocol: ") % protocols;
1153 handshakeRequest << QStringLiteral("\r\n");
1155 return handshakeRequest.join(QStringLiteral("\r\n"));
1161 QAbstractSocket::SocketState QWebSocketPrivate::state() const
1163 return m_socketState;
1169 bool QWebSocketPrivate::waitForConnected(int msecs)
1171 bool result = false;
1174 result = m_pSocket->waitForConnected(msecs);
1182 bool QWebSocketPrivate::waitForDisconnected(int msecs)
1184 bool result = false;
1187 result = m_pSocket->waitForDisconnected(msecs);
1195 void QWebSocketPrivate::setSocketState(QAbstractSocket::SocketState state)
1198 if (m_socketState != state)
1200 m_socketState = state;
1201 Q_EMIT q->stateChanged(m_socketState);
1208 void QWebSocketPrivate::setErrorString(const QString &errorString)
1210 m_errorString = errorString;
1216 QHostAddress QWebSocketPrivate::localAddress() const
1218 QHostAddress address;
1221 address = m_pSocket->localAddress();
1229 quint16 QWebSocketPrivate::localPort() const
1234 port = m_pSocket->localPort();
1242 QAbstractSocket::PauseModes QWebSocketPrivate::pauseMode() const
1244 QAbstractSocket::PauseModes mode = QAbstractSocket::PauseNever;
1247 mode = m_pSocket->pauseMode();
1255 QHostAddress QWebSocketPrivate::peerAddress() const
1257 QHostAddress address;
1260 address = m_pSocket->peerAddress();
1268 QString QWebSocketPrivate::peerName() const
1273 name = m_pSocket->peerName();
1281 quint16 QWebSocketPrivate::peerPort() const
1286 port = m_pSocket->peerPort();
1291 #ifndef QT_NO_NETWORKPROXY
1295 QNetworkProxy QWebSocketPrivate::proxy() const
1297 return m_configuration.m_proxy;
1303 void QWebSocketPrivate::setProxy(const QNetworkProxy &networkProxy)
1305 m_configuration.m_proxy = networkProxy;
1307 #endif //QT_NO_NETWORKPROXY
1312 qint64 QWebSocketPrivate::readBufferSize() const
1317 size = m_pSocket->readBufferSize();
1325 void QWebSocketPrivate::resume()
1329 m_pSocket->resume();
1336 void QWebSocketPrivate::setPauseMode(QAbstractSocket::PauseModes pauseMode)
1340 m_pSocket->setPauseMode(pauseMode);
1347 void QWebSocketPrivate::setReadBufferSize(qint64 size)
1351 m_pSocket->setReadBufferSize(size);
1358 void QWebSocketPrivate::setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value)
1362 m_pSocket->setSocketOption(option, value);
1369 QVariant QWebSocketPrivate::socketOption(QAbstractSocket::SocketOption option)
1374 val = m_pSocket->socketOption(option);
1382 bool QWebSocketPrivate::isValid() const
1384 return (m_pSocket && m_pSocket->isValid());