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.data());
146 QWebSocketPrivate::~QWebSocketPrivate()
151 if (state() == QAbstractSocket::ConnectedState) {
152 close(QWebSocketProtocol::CC_GOING_AWAY, tr("Connection closed"));
154 releaseConnections(m_pSocket.data());
160 void QWebSocketPrivate::abort()
170 QAbstractSocket::SocketError QWebSocketPrivate::error() const
172 QAbstractSocket::SocketError err = QAbstractSocket::OperationError;
173 if (Q_LIKELY(m_pSocket)) {
174 err = m_pSocket->error();
182 QString QWebSocketPrivate::errorString() const
185 if (!m_errorString.isEmpty()) {
186 errMsg = m_errorString;
187 } else if (m_pSocket) {
188 errMsg = m_pSocket->errorString();
196 bool QWebSocketPrivate::flush()
199 if (Q_LIKELY(m_pSocket)) {
200 result = m_pSocket->flush();
208 qint64 QWebSocketPrivate::write(const char *message)
210 return doWriteFrames(QByteArray::fromRawData(message, qstrlen(message)), false);
216 qint64 QWebSocketPrivate::write(const char *message, qint64 maxSize)
218 return write(QByteArray::fromRawData(message, maxSize), false);
224 qint64 QWebSocketPrivate::write(const QString &message)
226 return doWriteFrames(message.toUtf8(), false);
232 qint64 QWebSocketPrivate::write(const QByteArray &data)
234 return doWriteFrames(data, true);
241 void QWebSocketPrivate::setSslConfiguration(const QSslConfiguration &sslConfiguration)
243 m_configuration.m_sslConfiguration = sslConfiguration;
249 QSslConfiguration QWebSocketPrivate::sslConfiguration() const
251 return m_configuration.m_sslConfiguration;
257 void QWebSocketPrivate::ignoreSslErrors(const QList<QSslError> &errors)
259 m_configuration.m_ignoredSslErrors = errors;
265 void QWebSocketPrivate::ignoreSslErrors()
267 m_configuration.m_ignoreSslErrors = true;
268 if (Q_LIKELY(m_pSocket)) {
269 QSslSocket *pSslSocket = qobject_cast<QSslSocket *>(m_pSocket.data());
270 if (Q_LIKELY(pSslSocket)) {
271 pSslSocket->ignoreSslErrors();
279 Called from QWebSocketServer
282 QWebSocket *QWebSocketPrivate::upgradeFrom(QTcpSocket *pTcpSocket,
283 const QWebSocketHandshakeRequest &request,
284 const QWebSocketHandshakeResponse &response,
287 QWebSocket *pWebSocket = new QWebSocket(pTcpSocket, response.acceptedVersion(), parent);
288 if (Q_LIKELY(pWebSocket)) {
289 pWebSocket->d_func()->setExtension(response.acceptedExtension());
290 pWebSocket->d_func()->setOrigin(request.origin());
291 pWebSocket->d_func()->setRequestUrl(request.requestUrl());
292 pWebSocket->d_func()->setProtocol(response.acceptedProtocol());
293 pWebSocket->d_func()->setResourceName(request.requestUrl().toString(QUrl::RemoveUserInfo));
294 //a server should not send masked frames
295 pWebSocket->d_func()->enableMasking(false);
304 void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString reason)
306 if (Q_UNLIKELY(!m_pSocket)) {
309 if (!m_isClosingHandshakeSent) {
311 quint32 maskingKey = 0;
313 maskingKey = generateMaskingKey();
315 const quint16 code = qToBigEndian<quint16>(closeCode);
317 payload.append(static_cast<const char *>(static_cast<const void *>(&code)), 2);
318 if (!reason.isEmpty()) {
319 payload.append(reason.toUtf8());
322 QWebSocketProtocol::mask(payload.data(), payload.size(), maskingKey);
324 QByteArray frame = getFrameHeader(QWebSocketProtocol::OC_CLOSE, payload.size(), maskingKey, true);
325 frame.append(payload);
326 m_pSocket->write(frame);
329 m_isClosingHandshakeSent = true;
331 Q_EMIT q->aboutToClose();
339 void QWebSocketPrivate::open(const QUrl &url, bool mask)
341 //m_pSocket.reset(); //just delete the old socket for the moment; later, we can add more 'intelligent' handling by looking at the url
342 QTcpSocket *pTcpSocket = m_pSocket.take();
344 releaseConnections(pTcpSocket);
345 pTcpSocket->deleteLater();
348 if (Q_LIKELY(!m_pSocket)) {
351 m_dataProcessor.clear();
352 m_isClosingHandshakeReceived = false;
353 m_isClosingHandshakeSent = false;
356 QString resourceName = url.path();
357 if (!url.query().isEmpty()) {
358 if (!resourceName.endsWith(QChar::fromLatin1('?'))) {
359 resourceName.append(QChar::fromLatin1('?'));
361 resourceName.append(url.query());
363 if (resourceName.isEmpty()) {
364 resourceName = QStringLiteral("/");
366 setResourceName(resourceName);
370 if (url.scheme() == QStringLiteral("wss")) {
371 if (!QSslSocket::supportsSsl()) {
372 const QString message = tr("SSL Sockets are not supported on this platform.");
373 setErrorString(message);
374 emit q->error(QAbstractSocket::UnsupportedSocketOperationError);
376 QSslSocket *sslSocket = new QSslSocket(this);
377 m_pSocket.reset(sslSocket);
378 if (Q_LIKELY(m_pSocket)) {
379 m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
380 m_pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
382 makeConnections(m_pSocket.data());
383 connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)), q, SIGNAL(bytesWritten(qint64)));
384 setSocketState(QAbstractSocket::ConnectingState);
386 sslSocket->setSslConfiguration(m_configuration.m_sslConfiguration);
387 if (Q_UNLIKELY(m_configuration.m_ignoreSslErrors)) {
388 sslSocket->ignoreSslErrors();
390 sslSocket->ignoreSslErrors(m_configuration.m_ignoredSslErrors);
392 #ifndef QT_NO_NETWORKPROXY
393 sslSocket->setProxy(m_configuration.m_proxy);
395 sslSocket->connectToHostEncrypted(url.host(), url.port(443));
397 const QString message = tr("Out of memory.");
398 setErrorString(message);
399 emit q->error(QAbstractSocket::SocketResourceError);
404 if (url.scheme() == QStringLiteral("ws")) {
405 m_pSocket.reset(new QTcpSocket(this));
406 if (Q_LIKELY(m_pSocket)) {
407 m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
408 m_pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
410 makeConnections(m_pSocket.data());
411 connect(m_pSocket.data(), SIGNAL(bytesWritten(qint64)), q, SIGNAL(bytesWritten(qint64)));
412 setSocketState(QAbstractSocket::ConnectingState);
413 #ifndef QT_NO_NETWORKPROXY
414 m_pSocket->setProxy(m_configuration.m_proxy);
416 m_pSocket->connectToHost(url.host(), url.port(80));
418 const QString message = tr("Out of memory.");
419 setErrorString(message);
420 emit q->error(QAbstractSocket::SocketResourceError);
423 const QString message = tr("Unsupported websockets scheme: %1").arg(url.scheme());
424 setErrorString(message);
425 emit q->error(QAbstractSocket::UnsupportedSocketOperationError);
433 void QWebSocketPrivate::ping(QByteArray payload)
435 if (payload.length() > 125) {
436 payload.truncate(125);
438 m_pingTimer.restart();
439 QByteArray pingFrame = getFrameHeader(QWebSocketProtocol::OC_PING, payload.size(), 0 /*do not mask*/, true);
440 pingFrame.append(payload);
441 (void)writeFrame(pingFrame);
446 Sets the version to use for the websocket protocol; this must be set before the socket is opened.
448 void QWebSocketPrivate::setVersion(QWebSocketProtocol::Version version)
450 if (m_version != version) {
457 Sets the resource name of the connection; must be set before the socket is openend
459 void QWebSocketPrivate::setResourceName(const QString &resourceName)
461 if (m_resourceName != resourceName) {
462 m_resourceName = resourceName;
469 void QWebSocketPrivate::setRequestUrl(const QUrl &requestUrl)
471 if (m_requestUrl != requestUrl) {
472 m_requestUrl = requestUrl;
479 void QWebSocketPrivate::setOrigin(const QString &origin)
481 if (m_origin != origin) {
489 void QWebSocketPrivate::setProtocol(const QString &protocol)
491 if (m_protocol != protocol) {
492 m_protocol = protocol;
499 void QWebSocketPrivate::setExtension(const QString &extension)
501 if (m_extension != extension) {
502 m_extension = extension;
509 void QWebSocketPrivate::enableMasking(bool enable)
511 if (m_mustMask != enable) {
519 void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket)
521 Q_ASSERT(pTcpSocket);
524 if (Q_LIKELY(pTcpSocket)) {
525 //pass through signals
526 connect(pTcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), q, SIGNAL(error(QAbstractSocket::SocketError)));
527 connect(pTcpSocket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), q, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)));
528 connect(pTcpSocket, SIGNAL(readChannelFinished()), q, SIGNAL(readChannelFinished()));
529 connect(pTcpSocket, SIGNAL(aboutToClose()), q, SIGNAL(aboutToClose()));
532 connect(pTcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(processStateChanged(QAbstractSocket::SocketState)));
533 //!!!important to use a QueuedConnection here; with QTcpSocket there is no problem, but with QSslSocket the processing hangs
534 connect(pTcpSocket, SIGNAL(readyRead()), this, SLOT(processData()), Qt::QueuedConnection);
537 connect(&m_dataProcessor, SIGNAL(textFrameReceived(QString,bool)), q, SIGNAL(textFrameReceived(QString,bool)));
538 connect(&m_dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)), q, SIGNAL(binaryFrameReceived(QByteArray,bool)));
539 connect(&m_dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)), q, SIGNAL(binaryMessageReceived(QByteArray)));
540 connect(&m_dataProcessor, SIGNAL(textMessageReceived(QString)), q, SIGNAL(textMessageReceived(QString)));
541 connect(&m_dataProcessor, SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)), this, SLOT(close(QWebSocketProtocol::CloseCode,QString)));
542 connect(&m_dataProcessor, SIGNAL(pingReceived(QByteArray)), this, SLOT(processPing(QByteArray)));
543 connect(&m_dataProcessor, SIGNAL(pongReceived(QByteArray)), this, SLOT(processPong(QByteArray)));
544 connect(&m_dataProcessor, SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString)), this, SLOT(processClose(QWebSocketProtocol::CloseCode,QString)));
550 void QWebSocketPrivate::releaseConnections(const QTcpSocket *pTcpSocket)
552 if (Q_LIKELY(pTcpSocket)) {
553 disconnect(pTcpSocket);
555 disconnect(&m_dataProcessor);
561 QWebSocketProtocol::Version QWebSocketPrivate::version() const
569 QString QWebSocketPrivate::resourceName() const
571 return m_resourceName;
577 QUrl QWebSocketPrivate::requestUrl() const
585 QString QWebSocketPrivate::origin() const
593 QString QWebSocketPrivate::protocol() const
601 QString QWebSocketPrivate::extension() const
609 QWebSocketProtocol::CloseCode QWebSocketPrivate::closeCode() const
617 QString QWebSocketPrivate::closeReason() const
619 return m_closeReason;
625 QByteArray QWebSocketPrivate::getFrameHeader(QWebSocketProtocol::OpCode opCode, quint64 payloadLength, quint32 maskingKey, bool lastFrame)
629 bool ok = payloadLength <= 0x7FFFFFFFFFFFFFFFULL;
632 //FIN, RSV1-3, opcode
633 byte = static_cast<quint8>((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00)); //FIN, opcode
634 //RSV-1, RSV-2 and RSV-3 are zero
635 header.append(static_cast<char>(byte));
638 if (maskingKey != 0) {
641 if (payloadLength <= 125) {
642 byte |= static_cast<quint8>(payloadLength);
643 header.append(static_cast<char>(byte));
644 } else if (payloadLength <= 0xFFFFU) {
646 header.append(static_cast<char>(byte));
647 quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
648 header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
649 } else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL) {
651 header.append(static_cast<char>(byte));
652 quint64 swapped = qToBigEndian<quint64>(payloadLength);
653 header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8);
656 if (maskingKey != 0) {
657 const quint32 mask = qToBigEndian<quint32>(maskingKey);
658 header.append(static_cast<const char *>(static_cast<const void *>(&mask)), sizeof(quint32));
661 setErrorString(QStringLiteral("WebSocket::getHeader: payload too big!"));
662 Q_EMIT q_ptr->error(QAbstractSocket::DatagramTooLargeError);
671 qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary)
673 qint64 payloadWritten = 0;
674 if (Q_UNLIKELY(!m_pSocket)) {
675 return payloadWritten;
678 const QWebSocketProtocol::OpCode firstOpCode = isBinary ?
679 QWebSocketProtocol::OC_BINARY : QWebSocketProtocol::OC_TEXT;
681 int numFrames = data.size() / FRAME_SIZE_IN_BYTES;
682 QByteArray tmpData(data);
684 char *payload = tmpData.data();
685 quint64 sizeLeft = quint64(data.size()) % FRAME_SIZE_IN_BYTES;
686 if (Q_LIKELY(sizeLeft)) {
689 //catch the case where the payload is zero bytes;
690 //in this case, we still need to send a frame
691 if (Q_UNLIKELY(numFrames == 0)) {
694 quint64 currentPosition = 0;
695 qint64 bytesWritten = 0;
696 quint64 bytesLeft = data.size();
698 for (int i = 0; i < numFrames; ++i) {
699 quint32 maskingKey = 0;
701 maskingKey = generateMaskingKey();
704 const bool isLastFrame = (i == (numFrames - 1));
705 const bool isFirstFrame = (i == 0);
707 const quint64 size = qMin(bytesLeft, FRAME_SIZE_IN_BYTES);
708 const QWebSocketProtocol::OpCode opcode = isFirstFrame ? firstOpCode : QWebSocketProtocol::OC_CONTINUE;
711 bytesWritten += m_pSocket->write(getFrameHeader(opcode, size, maskingKey, isLastFrame));
714 if (Q_LIKELY(size > 0)) {
715 char *currentData = payload + currentPosition;
717 QWebSocketProtocol::mask(currentData, size, maskingKey);
719 qint64 written = m_pSocket->write(currentData, static_cast<qint64>(size));
720 if (Q_LIKELY(written > 0)) {
721 bytesWritten += written;
722 payloadWritten += written;
725 setErrorString(tr("Error writing bytes to socket: %1.").arg(m_pSocket->errorString()));
726 Q_EMIT q->error(QAbstractSocket::NetworkError);
730 currentPosition += size;
733 if (Q_UNLIKELY(payloadWritten != data.size())) {
734 setErrorString(tr("Bytes written %1 != %2.").arg(payloadWritten).arg(data.size()));
735 Q_EMIT q->error(QAbstractSocket::NetworkError);
737 return payloadWritten;
743 quint32 QWebSocketPrivate::generateRandomNumber() const
745 return quint32((double(qrand()) / RAND_MAX) * std::numeric_limits<quint32>::max());
751 quint32 QWebSocketPrivate::generateMaskingKey() const
753 return generateRandomNumber();
759 QByteArray QWebSocketPrivate::generateKey() const
763 for (int i = 0; i < 4; ++i) {
764 const quint32 tmp = generateRandomNumber();
765 key.append(static_cast<const char *>(static_cast<const void *>(&tmp)), sizeof(quint32));
768 return key.toBase64();
775 QString QWebSocketPrivate::calculateAcceptKey(const QByteArray &key) const
777 const QByteArray tmpKey = key + QByteArrayLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
778 const QByteArray hash = QCryptographicHash::hash(tmpKey, QCryptographicHash::Sha1).toBase64();
779 return QString::fromLatin1(hash);
785 qint64 QWebSocketPrivate::writeFrames(const QList<QByteArray> &frames)
788 if (Q_LIKELY(m_pSocket)) {
789 QList<QByteArray>::const_iterator it;
790 for (it = frames.cbegin(); it < frames.cend(); ++it) {
791 written += writeFrame(*it);
800 qint64 QWebSocketPrivate::writeFrame(const QByteArray &frame)
803 if (Q_LIKELY(m_pSocket)) {
804 written = m_pSocket->write(frame);
812 QString readLine(QTcpSocket *pSocket)
817 while (pSocket->getChar(&c)) {
818 if (c == char('\r')) {
819 pSocket->getChar(&c);
822 line.append(QChar::fromLatin1(c));
828 //called on the client for a server handshake response
832 void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket)
840 QString errorDescription;
842 const QString regExpStatusLine(QStringLiteral("^(HTTP/[0-9]+\\.[0-9]+)\\s([0-9]+)\\s(.*)"));
843 const QRegularExpression regExp(regExpStatusLine);
844 const QString statusLine = readLine(pSocket);
845 QString httpProtocol;
847 QString httpStatusMessage;
848 const QRegularExpressionMatch match = regExp.match(statusLine);
849 if (Q_LIKELY(match.hasMatch())) {
850 QStringList tokens = match.capturedTexts();
851 tokens.removeFirst(); //remove the search string
852 if (tokens.length() == 3) {
853 httpProtocol = tokens[0];
854 httpStatusCode = tokens[1].toInt();
855 httpStatusMessage = tokens[2].trimmed();
859 if (Q_UNLIKELY(!ok)) {
860 errorDescription = tr("Invalid statusline in response: %1.").arg(statusLine);
862 QString headerLine = readLine(pSocket);
863 QMap<QString, QString> headers;
864 while (!headerLine.isEmpty()) {
865 const QStringList headerField = headerLine.split(QStringLiteral(": "), QString::SkipEmptyParts);
866 headers.insertMulti(headerField[0], headerField[1]);
867 headerLine = readLine(pSocket);
870 const QString acceptKey = headers.value(QStringLiteral("Sec-WebSocket-Accept"), QStringLiteral(""));
871 const QString upgrade = headers.value(QStringLiteral("Upgrade"), QStringLiteral(""));
872 const QString connection = headers.value(QStringLiteral("Connection"), QStringLiteral(""));
873 //unused for the moment
874 //const QString extensions = headers.value(QStringLiteral("Sec-WebSocket-Extensions"), QStringLiteral(""));
875 //const QString protocol = headers.value(QStringLiteral("Sec-WebSocket-Protocol"), QStringLiteral(""));
876 const QString version = headers.value(QStringLiteral("Sec-WebSocket-Version"), QStringLiteral(""));
878 if (Q_LIKELY(httpStatusCode == 101)) {
879 //HTTP/x.y 101 Switching Protocols
880 bool conversionOk = false;
881 const float version = httpProtocol.midRef(5).toFloat(&conversionOk);
882 //TODO: do not check the httpStatusText right now
883 ok = !(acceptKey.isEmpty() ||
884 (!conversionOk || (version < 1.1f)) ||
885 (upgrade.toLower() != QStringLiteral("websocket")) ||
886 (connection.toLower() != QStringLiteral("upgrade")));
888 const QString accept = calculateAcceptKey(m_key);
889 ok = (accept == acceptKey);
891 errorDescription = tr("Accept-Key received from server %1 does not match the client key %2.").arg(acceptKey).arg(accept);
894 errorDescription = tr("QWebSocketPrivate::processHandshake: Invalid statusline in response: %1.").arg(statusLine);
896 } else if (httpStatusCode == 400) {
897 //HTTP/1.1 400 Bad Request
898 if (!version.isEmpty()) {
899 const QStringList versions = version.split(QStringLiteral(", "), QString::SkipEmptyParts);
900 if (!versions.contains(QString::number(QWebSocketProtocol::currentVersion()))) {
901 //if needed to switch protocol version, then we are finished here
902 //because we cannot handle other protocols than the RFC one (v13)
903 errorDescription = tr("Handshake: Server requests a version that we don't support: %1.").arg(versions.join(QStringLiteral(", ")));
906 //we tried v13, but something different went wrong
907 errorDescription = tr("QWebSocketPrivate::processHandshake: Unknown error condition encountered. Aborting connection.");
912 errorDescription = tr("QWebSocketPrivate::processHandshake: Unhandled http status code: %1 (%2).").arg(httpStatusCode).arg(httpStatusMessage);
917 setErrorString(errorDescription);
918 Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
920 //handshake succeeded
921 setSocketState(QAbstractSocket::ConnectedState);
922 Q_EMIT q->connected();
930 void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketState)
934 QAbstractSocket::SocketState webSocketState = this->state();
937 case QAbstractSocket::ConnectedState:
939 if (webSocketState == QAbstractSocket::ConnectingState) {
940 m_key = generateKey();
941 const QString handshake = createHandShakeRequest(m_resourceName, m_requestUrl.host() % QStringLiteral(":") % QString::number(m_requestUrl.port(80)), origin(), QStringLiteral(""), QStringLiteral(""), m_key);
942 m_pSocket->write(handshake.toLatin1());
946 case QAbstractSocket::ClosingState:
948 if (webSocketState == QAbstractSocket::ConnectedState) {
949 setSocketState(QAbstractSocket::ClosingState);
953 case QAbstractSocket::UnconnectedState:
955 if (webSocketState != QAbstractSocket::UnconnectedState) {
956 setSocketState(QAbstractSocket::UnconnectedState);
957 Q_EMIT q->disconnected();
961 case QAbstractSocket::HostLookupState:
962 case QAbstractSocket::ConnectingState:
963 case QAbstractSocket::BoundState:
964 case QAbstractSocket::ListeningState:
967 //to make C++ compiler happy;
978 //connectToHost is called
979 //our socket state is set to "connecting", and tcpSocket->connectToHost is called
980 //the tcpsocket is opened, a handshake message is sent; a readyRead signal is thrown
981 //this signal is catched by processData
982 //when OUR socket state is in the "connecting state", this means that
983 //we have received data from the server (response to handshake), and that we
984 //should "upgrade" our socket to a websocket (connected state)
985 //if our socket was already upgraded, then we need to process websocket data
989 void QWebSocketPrivate::processData()
992 while (m_pSocket->bytesAvailable()) {
993 if (state() == QAbstractSocket::ConnectingState) {
994 processHandshake(m_pSocket.data());
996 m_dataProcessor.process(m_pSocket.data());
1004 void QWebSocketPrivate::processPing(QByteArray data)
1006 Q_ASSERT(m_pSocket);
1007 quint32 maskingKey = 0;
1009 maskingKey = generateMaskingKey();
1011 m_pSocket->write(getFrameHeader(QWebSocketProtocol::OC_PONG, data.size(), maskingKey, true));
1012 if (data.size() > 0) {
1014 QWebSocketProtocol::mask(&data, maskingKey);
1016 m_pSocket->write(data);
1023 void QWebSocketPrivate::processPong(QByteArray data)
1026 Q_EMIT q->pong(static_cast<quint64>(m_pingTimer.elapsed()), data);
1032 void QWebSocketPrivate::processClose(QWebSocketProtocol::CloseCode closeCode, QString closeReason)
1034 m_isClosingHandshakeReceived = true;
1035 close(closeCode, closeReason);
1041 QString QWebSocketPrivate::createHandShakeRequest(QString resourceName,
1048 QStringList handshakeRequest;
1050 handshakeRequest << QStringLiteral("GET ") % resourceName % QStringLiteral(" HTTP/1.1") <<
1051 QStringLiteral("Host: ") % host <<
1052 QStringLiteral("Upgrade: websocket") <<
1053 QStringLiteral("Connection: Upgrade") <<
1054 QStringLiteral("Sec-WebSocket-Key: ") % QString::fromLatin1(key);
1055 if (!origin.isEmpty()) {
1056 handshakeRequest << QStringLiteral("Origin: ") % origin;
1058 handshakeRequest << QStringLiteral("Sec-WebSocket-Version: ") % QString::number(QWebSocketProtocol::currentVersion());
1059 if (extensions.length() > 0) {
1060 handshakeRequest << QStringLiteral("Sec-WebSocket-Extensions: ") % extensions;
1062 if (protocols.length() > 0) {
1063 handshakeRequest << QStringLiteral("Sec-WebSocket-Protocol: ") % protocols;
1065 handshakeRequest << QStringLiteral("\r\n");
1067 return handshakeRequest.join(QStringLiteral("\r\n"));
1073 QAbstractSocket::SocketState QWebSocketPrivate::state() const
1075 return m_socketState;
1081 bool QWebSocketPrivate::waitForConnected(int msecs)
1083 bool result = false;
1084 if (Q_LIKELY(m_pSocket)) {
1085 result = m_pSocket->waitForConnected(msecs);
1093 bool QWebSocketPrivate::waitForDisconnected(int msecs)
1095 bool result = false;
1096 if (Q_LIKELY(m_pSocket)) {
1097 result = m_pSocket->waitForDisconnected(msecs);
1105 void QWebSocketPrivate::setSocketState(QAbstractSocket::SocketState state)
1108 if (m_socketState != state) {
1109 m_socketState = state;
1110 Q_EMIT q->stateChanged(m_socketState);
1117 void QWebSocketPrivate::setErrorString(const QString &errorString)
1119 if (m_errorString != errorString) {
1120 m_errorString = errorString;
1127 QHostAddress QWebSocketPrivate::localAddress() const
1129 QHostAddress address;
1130 if (Q_LIKELY(m_pSocket)) {
1131 address = m_pSocket->localAddress();
1139 quint16 QWebSocketPrivate::localPort() const
1142 if (Q_LIKELY(m_pSocket)) {
1143 port = m_pSocket->localPort();
1151 QAbstractSocket::PauseModes QWebSocketPrivate::pauseMode() const
1153 QAbstractSocket::PauseModes mode = QAbstractSocket::PauseNever;
1154 if (Q_LIKELY(m_pSocket)) {
1155 mode = m_pSocket->pauseMode();
1163 QHostAddress QWebSocketPrivate::peerAddress() const
1165 QHostAddress address;
1166 if (Q_LIKELY(m_pSocket)) {
1167 address = m_pSocket->peerAddress();
1175 QString QWebSocketPrivate::peerName() const
1178 if (Q_LIKELY(m_pSocket)) {
1179 name = m_pSocket->peerName();
1187 quint16 QWebSocketPrivate::peerPort() const
1190 if (Q_LIKELY(m_pSocket)) {
1191 port = m_pSocket->peerPort();
1196 #ifndef QT_NO_NETWORKPROXY
1200 QNetworkProxy QWebSocketPrivate::proxy() const
1202 return m_configuration.m_proxy;
1208 void QWebSocketPrivate::setProxy(const QNetworkProxy &networkProxy)
1210 if (networkProxy != networkProxy) {
1211 m_configuration.m_proxy = networkProxy;
1214 #endif //QT_NO_NETWORKPROXY
1219 qint64 QWebSocketPrivate::readBufferSize() const
1222 if (Q_LIKELY(m_pSocket)) {
1223 size = m_pSocket->readBufferSize();
1231 void QWebSocketPrivate::resume()
1233 if (Q_LIKELY(m_pSocket)) {
1234 m_pSocket->resume();
1241 void QWebSocketPrivate::setPauseMode(QAbstractSocket::PauseModes pauseMode)
1243 if (Q_LIKELY(m_pSocket)) {
1244 m_pSocket->setPauseMode(pauseMode);
1251 void QWebSocketPrivate::setReadBufferSize(qint64 size)
1253 if (Q_LIKELY(m_pSocket)) {
1254 m_pSocket->setReadBufferSize(size);
1261 void QWebSocketPrivate::setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value)
1263 if (Q_LIKELY(m_pSocket)) {
1264 m_pSocket->setSocketOption(option, value);
1271 QVariant QWebSocketPrivate::socketOption(QAbstractSocket::SocketOption option)
1274 if (Q_LIKELY(m_pSocket)) {
1275 val = m_pSocket->socketOption(option);
1283 bool QWebSocketPrivate::isValid() const
1285 return (m_pSocket && m_pSocket->isValid());