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) :
94 m_errorString(QWebSocket::tr("Unknown error")),
101 m_socketState(QAbstractSocket::UnconnectedState),
102 m_pauseMode(QAbstractSocket::PauseNever),
106 m_isClosingHandshakeSent(false),
107 m_isClosingHandshakeReceived(false),
108 m_closeCode(QWebSocketProtocol::CloseCodeNormal),
119 QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version,
120 QWebSocket *pWebSocket) :
123 m_pSocket(pTcpSocket),
124 m_errorString(pTcpSocket->errorString()),
131 m_socketState(pTcpSocket->state()),
132 m_pauseMode(pTcpSocket->pauseMode()),
133 m_readBufferSize(pTcpSocket->readBufferSize()),
136 m_isClosingHandshakeSent(false),
137 m_isClosingHandshakeReceived(false),
138 m_closeCode(QWebSocketProtocol::CloseCodeNormal),
149 void QWebSocketPrivate::init()
152 //TODO: need a better randomizer
153 qsrand(static_cast<uint>(QDateTime::currentMSecsSinceEpoch()));
156 makeConnections(m_pSocket.data());
163 QWebSocketPrivate::~QWebSocketPrivate()
167 if (state() == QAbstractSocket::ConnectedState)
168 close(QWebSocketProtocol::CloseCodeGoingAway, QWebSocket::tr("Connection closed"));
169 releaseConnections(m_pSocket.data());
175 void QWebSocketPrivate::abort()
184 QAbstractSocket::SocketError QWebSocketPrivate::error() const
186 QAbstractSocket::SocketError err = QAbstractSocket::UnknownSocketError;
187 if (Q_LIKELY(m_pSocket))
188 err = m_pSocket->error();
195 QString QWebSocketPrivate::errorString() const
198 if (!m_errorString.isEmpty())
199 errMsg = m_errorString;
201 errMsg = m_pSocket->errorString();
208 bool QWebSocketPrivate::flush()
211 if (Q_LIKELY(m_pSocket))
212 result = m_pSocket->flush();
219 qint64 QWebSocketPrivate::sendTextMessage(const QString &message)
221 return doWriteFrames(message.toUtf8(), false);
227 qint64 QWebSocketPrivate::sendBinaryMessage(const QByteArray &data)
229 return doWriteFrames(data, true);
236 void QWebSocketPrivate::setSslConfiguration(const QSslConfiguration &sslConfiguration)
238 m_configuration.m_sslConfiguration = sslConfiguration;
244 QSslConfiguration QWebSocketPrivate::sslConfiguration() const
246 return m_configuration.m_sslConfiguration;
252 void QWebSocketPrivate::ignoreSslErrors(const QList<QSslError> &errors)
254 m_configuration.m_ignoredSslErrors = errors;
260 void QWebSocketPrivate::ignoreSslErrors()
262 m_configuration.m_ignoreSslErrors = true;
263 if (Q_LIKELY(m_pSocket)) {
264 QSslSocket *pSslSocket = qobject_cast<QSslSocket *>(m_pSocket.data());
265 if (Q_LIKELY(pSslSocket))
266 pSslSocket->ignoreSslErrors();
273 Called from QWebSocketServer
276 QWebSocket *QWebSocketPrivate::upgradeFrom(QTcpSocket *pTcpSocket,
277 const QWebSocketHandshakeRequest &request,
278 const QWebSocketHandshakeResponse &response,
281 QWebSocket *pWebSocket = new QWebSocket(pTcpSocket, response.acceptedVersion(), parent);
282 if (Q_LIKELY(pWebSocket)) {
283 pWebSocket->d_func()->setExtension(response.acceptedExtension());
284 pWebSocket->d_func()->setOrigin(request.origin());
285 pWebSocket->d_func()->setRequestUrl(request.requestUrl());
286 pWebSocket->d_func()->setProtocol(response.acceptedProtocol());
287 pWebSocket->d_func()->setResourceName(request.requestUrl().toString(QUrl::RemoveUserInfo));
288 //a server should not send masked frames
289 pWebSocket->d_func()->enableMasking(false);
298 void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString reason)
300 if (Q_UNLIKELY(!m_pSocket))
302 if (!m_isClosingHandshakeSent) {
304 const quint16 code = qToBigEndian<quint16>(closeCode);
306 payload.append(static_cast<const char *>(static_cast<const void *>(&code)), 2);
307 if (!reason.isEmpty())
308 payload.append(reason.toUtf8());
309 quint32 maskingKey = 0;
311 maskingKey = generateMaskingKey();
312 QWebSocketProtocol::mask(payload.data(), payload.size(), maskingKey);
314 QByteArray frame = getFrameHeader(QWebSocketProtocol::OpCodeClose,
315 payload.size(), maskingKey, true);
316 frame.append(payload);
317 m_pSocket->write(frame);
320 m_isClosingHandshakeSent = true;
322 Q_EMIT q->aboutToClose();
330 void QWebSocketPrivate::open(const QUrl &url, bool mask)
332 //just delete the old socket for the moment;
333 //later, we can add more 'intelligent' handling by looking at the url
335 QTcpSocket *pTcpSocket = m_pSocket.take();
337 releaseConnections(pTcpSocket);
338 pTcpSocket->deleteLater();
341 if (Q_LIKELY(!m_pSocket)) {
344 m_dataProcessor.clear();
345 m_isClosingHandshakeReceived = false;
346 m_isClosingHandshakeSent = false;
349 QString resourceName = url.path();
350 if (!url.query().isEmpty()) {
351 if (!resourceName.endsWith(QChar::fromLatin1('?'))) {
352 resourceName.append(QChar::fromLatin1('?'));
354 resourceName.append(url.query());
356 if (resourceName.isEmpty())
357 resourceName = QStringLiteral("/");
358 setResourceName(resourceName);
362 if (url.scheme() == QStringLiteral("wss")) {
363 if (!QSslSocket::supportsSsl()) {
364 const QString message =
365 QWebSocket::tr("SSL Sockets are not supported on this platform.");
366 setErrorString(message);
367 Q_EMIT q->error(QAbstractSocket::UnsupportedSocketOperationError);
369 QSslSocket *sslSocket = new QSslSocket;
370 m_pSocket.reset(sslSocket);
371 if (Q_LIKELY(m_pSocket)) {
372 m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
373 m_pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
374 m_pSocket->setReadBufferSize(m_readBufferSize);
375 m_pSocket->setPauseMode(m_pauseMode);
377 makeConnections(m_pSocket.data());
378 QObject::connect(sslSocket, &QSslSocket::encryptedBytesWritten, q,
379 &QWebSocket::bytesWritten);
380 typedef void (QSslSocket:: *sslErrorSignalType)(const QList<QSslError> &);
381 QObject::connect(sslSocket,
382 static_cast<sslErrorSignalType>(&QSslSocket::sslErrors),
383 q, &QWebSocket::sslErrors);
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);
391 #ifndef QT_NO_NETWORKPROXY
392 sslSocket->setProxy(m_configuration.m_proxy);
394 sslSocket->connectToHostEncrypted(url.host(), url.port(443));
396 const QString message = QWebSocket::tr("Out of memory.");
397 setErrorString(message);
398 Q_EMIT q->error(QAbstractSocket::SocketResourceError);
403 if (url.scheme() == QStringLiteral("ws")) {
404 m_pSocket.reset(new QTcpSocket);
405 if (Q_LIKELY(m_pSocket)) {
406 m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
407 m_pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
408 m_pSocket->setReadBufferSize(m_readBufferSize);
409 m_pSocket->setPauseMode(m_pauseMode);
411 makeConnections(m_pSocket.data());
412 QObject::connect(m_pSocket.data(), &QAbstractSocket::bytesWritten, q,
413 &QWebSocket::bytesWritten);
414 setSocketState(QAbstractSocket::ConnectingState);
415 #ifndef QT_NO_NETWORKPROXY
416 m_pSocket->setProxy(m_configuration.m_proxy);
418 m_pSocket->connectToHost(url.host(), url.port(80));
420 const QString message = QWebSocket::tr("Out of memory.");
421 setErrorString(message);
422 Q_EMIT q->error(QAbstractSocket::SocketResourceError);
425 const QString message =
426 QWebSocket::tr("Unsupported websockets scheme: %1").arg(url.scheme());
427 setErrorString(message);
428 Q_EMIT q->error(QAbstractSocket::UnsupportedSocketOperationError);
436 void QWebSocketPrivate::ping(const QByteArray &payload)
438 QByteArray payloadTruncated = payload.left(125);
439 m_pingTimer.restart();
440 QByteArray pingFrame = getFrameHeader(QWebSocketProtocol::OpCodePing, payloadTruncated.size(),
441 0 /*do not mask*/, true);
442 pingFrame.append(payloadTruncated);
443 qint64 ret = writeFrame(pingFrame);
449 Sets the version to use for the websocket protocol;
450 this must be set before the socket is opened.
452 void QWebSocketPrivate::setVersion(QWebSocketProtocol::Version version)
454 if (m_version != version)
460 Sets the resource name of the connection; must be set before the socket is openend
462 void QWebSocketPrivate::setResourceName(const QString &resourceName)
464 if (m_resourceName != resourceName)
465 m_resourceName = resourceName;
471 void QWebSocketPrivate::setRequestUrl(const QUrl &requestUrl)
473 if (m_requestUrl != requestUrl)
474 m_requestUrl = requestUrl;
480 void QWebSocketPrivate::setOrigin(const QString &origin)
482 if (m_origin != origin)
489 void QWebSocketPrivate::setProtocol(const QString &protocol)
491 if (m_protocol != protocol)
492 m_protocol = protocol;
498 void QWebSocketPrivate::setExtension(const QString &extension)
500 if (m_extension != extension)
501 m_extension = extension;
507 void QWebSocketPrivate::enableMasking(bool enable)
509 if (m_mustMask != enable)
516 void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket)
518 Q_ASSERT(pTcpSocket);
521 if (Q_LIKELY(pTcpSocket)) {
522 //pass through signals
523 typedef void (QAbstractSocket:: *ASErrorSignal)(QAbstractSocket::SocketError);
524 typedef void (QWebSocket:: *WSErrorSignal)(QAbstractSocket::SocketError);
525 QObject::connect(pTcpSocket,
526 static_cast<ASErrorSignal>(&QAbstractSocket::error),
527 q, static_cast<WSErrorSignal>(&QWebSocket::error));
528 QObject::connect(pTcpSocket, &QAbstractSocket::proxyAuthenticationRequired, q,
529 &QWebSocket::proxyAuthenticationRequired);
530 QObject::connect(pTcpSocket, &QAbstractSocket::readChannelFinished, q,
531 &QWebSocket::readChannelFinished);
532 QObject::connect(pTcpSocket, &QAbstractSocket::aboutToClose, q, &QWebSocket::aboutToClose);
535 QObjectPrivate::connect(pTcpSocket, &QAbstractSocket::stateChanged, this,
536 &QWebSocketPrivate::processStateChanged);
537 //!!!important to use a QueuedConnection here;
538 //with QTcpSocket there is no problem, but with QSslSocket the processing hangs
539 QObjectPrivate::connect(pTcpSocket, &QAbstractSocket::readyRead, this,
540 &QWebSocketPrivate::processData, Qt::QueuedConnection);
543 QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::textFrameReceived, q,
544 &QWebSocket::textFrameReceived);
545 QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryFrameReceived, q,
546 &QWebSocket::binaryFrameReceived);
547 QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryMessageReceived, q,
548 &QWebSocket::binaryMessageReceived);
549 QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::textMessageReceived, q,
550 &QWebSocket::textMessageReceived);
551 QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::errorEncountered, this,
552 &QWebSocketPrivate::close);
553 QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::pingReceived, this,
554 &QWebSocketPrivate::processPing);
555 QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::pongReceived, this,
556 &QWebSocketPrivate::processPong);
557 QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::closeReceived, this,
558 &QWebSocketPrivate::processClose);
564 void QWebSocketPrivate::releaseConnections(const QTcpSocket *pTcpSocket)
566 if (Q_LIKELY(pTcpSocket))
567 pTcpSocket->disconnect(pTcpSocket);
568 m_dataProcessor.disconnect(&m_dataProcessor);
574 QWebSocketProtocol::Version QWebSocketPrivate::version() const
582 QString QWebSocketPrivate::resourceName() const
584 return m_resourceName;
590 QUrl QWebSocketPrivate::requestUrl() const
598 QString QWebSocketPrivate::origin() const
606 QString QWebSocketPrivate::protocol() const
614 QString QWebSocketPrivate::extension() const
622 QWebSocketProtocol::CloseCode QWebSocketPrivate::closeCode() const
630 QString QWebSocketPrivate::closeReason() const
632 return m_closeReason;
638 QByteArray QWebSocketPrivate::getFrameHeader(QWebSocketProtocol::OpCode opCode,
639 quint64 payloadLength, quint32 maskingKey,
644 bool ok = payloadLength <= 0x7FFFFFFFFFFFFFFFULL;
647 //FIN, RSV1-3, opcode (RSV-1, RSV-2 and RSV-3 are zero)
648 byte = static_cast<quint8>((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00));
649 header.append(static_cast<char>(byte));
654 if (payloadLength <= 125) {
655 byte |= static_cast<quint8>(payloadLength);
656 header.append(static_cast<char>(byte));
657 } else if (payloadLength <= 0xFFFFU) {
659 header.append(static_cast<char>(byte));
660 quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
661 header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
662 } else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL) {
664 header.append(static_cast<char>(byte));
665 quint64 swapped = qToBigEndian<quint64>(payloadLength);
666 header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8);
669 if (maskingKey != 0) {
670 const quint32 mask = qToBigEndian<quint32>(maskingKey);
671 header.append(static_cast<const char *>(static_cast<const void *>(&mask)),
675 setErrorString(QStringLiteral("WebSocket::getHeader: payload too big!"));
676 Q_EMIT q_ptr->error(QAbstractSocket::DatagramTooLargeError);
685 qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary)
687 qint64 payloadWritten = 0;
688 if (Q_UNLIKELY(!m_pSocket) || (state() != QAbstractSocket::ConnectedState))
689 return payloadWritten;
692 const QWebSocketProtocol::OpCode firstOpCode = isBinary ?
693 QWebSocketProtocol::OpCodeBinary : QWebSocketProtocol::OpCodeText;
695 int numFrames = data.size() / FRAME_SIZE_IN_BYTES;
696 QByteArray tmpData(data);
698 char *payload = tmpData.data();
699 quint64 sizeLeft = quint64(data.size()) % FRAME_SIZE_IN_BYTES;
700 if (Q_LIKELY(sizeLeft))
703 //catch the case where the payload is zero bytes;
704 //in this case, we still need to send a frame
705 if (Q_UNLIKELY(numFrames == 0))
707 quint64 currentPosition = 0;
708 qint64 bytesWritten = 0;
709 quint64 bytesLeft = data.size();
711 for (int i = 0; i < numFrames; ++i) {
712 quint32 maskingKey = 0;
714 maskingKey = generateMaskingKey();
716 const bool isLastFrame = (i == (numFrames - 1));
717 const bool isFirstFrame = (i == 0);
719 const quint64 size = qMin(bytesLeft, FRAME_SIZE_IN_BYTES);
720 const QWebSocketProtocol::OpCode opcode = isFirstFrame ? firstOpCode
721 : QWebSocketProtocol::OpCodeContinue;
724 bytesWritten += m_pSocket->write(getFrameHeader(opcode, size, maskingKey, isLastFrame));
727 if (Q_LIKELY(size > 0)) {
728 char *currentData = payload + currentPosition;
730 QWebSocketProtocol::mask(currentData, size, maskingKey);
731 qint64 written = m_pSocket->write(currentData, static_cast<qint64>(size));
732 if (Q_LIKELY(written > 0)) {
733 bytesWritten += written;
734 payloadWritten += written;
737 setErrorString(QWebSocket::tr("Error writing bytes to socket: %1.")
738 .arg(m_pSocket->errorString()));
739 Q_EMIT q->error(QAbstractSocket::NetworkError);
743 currentPosition += size;
746 if (Q_UNLIKELY(payloadWritten != data.size())) {
747 setErrorString(QWebSocket::tr("Bytes written %1 != %2.")
748 .arg(payloadWritten).arg(data.size()));
749 Q_EMIT q->error(QAbstractSocket::NetworkError);
751 return payloadWritten;
757 quint32 QWebSocketPrivate::generateRandomNumber() const
759 //TODO: need a better randomizer
760 return quint32((double(qrand()) / RAND_MAX) * std::numeric_limits<quint32>::max());
766 quint32 QWebSocketPrivate::generateMaskingKey() const
768 return generateRandomNumber();
774 QByteArray QWebSocketPrivate::generateKey() const
778 for (int i = 0; i < 4; ++i) {
779 const quint32 tmp = generateRandomNumber();
780 key.append(static_cast<const char *>(static_cast<const void *>(&tmp)), sizeof(quint32));
783 return key.toBase64();
790 QString QWebSocketPrivate::calculateAcceptKey(const QByteArray &key) const
792 const QByteArray tmpKey = key + QByteArrayLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
793 const QByteArray hash = QCryptographicHash::hash(tmpKey, QCryptographicHash::Sha1).toBase64();
794 return QString::fromLatin1(hash);
800 qint64 QWebSocketPrivate::writeFrames(const QList<QByteArray> &frames)
803 if (Q_LIKELY(m_pSocket)) {
804 QList<QByteArray>::const_iterator it;
805 for (it = frames.cbegin(); it < frames.cend(); ++it)
806 written += writeFrame(*it);
814 qint64 QWebSocketPrivate::writeFrame(const QByteArray &frame)
817 if (Q_LIKELY(m_pSocket))
818 written = m_pSocket->write(frame);
825 QString readLine(QTcpSocket *pSocket)
830 while (pSocket->getChar(&c)) {
831 if (c == char('\r')) {
832 pSocket->getChar(&c);
835 line.append(QChar::fromLatin1(c));
841 //called on the client for a server handshake response
845 void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket)
848 if (Q_UNLIKELY(!pSocket))
852 QString errorDescription;
854 const QString regExpStatusLine(QStringLiteral("^(HTTP/[0-9]+\\.[0-9]+)\\s([0-9]+)\\s(.*)"));
855 const QRegularExpression regExp(regExpStatusLine);
856 const QString statusLine = readLine(pSocket);
857 QString httpProtocol;
859 QString httpStatusMessage;
860 const QRegularExpressionMatch match = regExp.match(statusLine);
861 if (Q_LIKELY(match.hasMatch())) {
862 QStringList tokens = match.capturedTexts();
863 tokens.removeFirst(); //remove the search string
864 if (tokens.length() == 3) {
865 httpProtocol = tokens[0];
866 httpStatusCode = tokens[1].toInt();
867 httpStatusMessage = tokens[2].trimmed();
871 if (Q_UNLIKELY(!ok)) {
872 errorDescription = QWebSocket::tr("Invalid statusline in response: %1.").arg(statusLine);
874 QString headerLine = readLine(pSocket);
875 QMap<QString, QString> headers;
876 while (!headerLine.isEmpty()) {
877 const QStringList headerField = headerLine.split(QStringLiteral(": "),
878 QString::SkipEmptyParts);
879 if (headerField.size() == 2) {
880 headers.insertMulti(headerField[0], headerField[1]);
882 headerLine = readLine(pSocket);
885 const QString acceptKey = headers.value(QStringLiteral("Sec-WebSocket-Accept"),
887 const QString upgrade = headers.value(QStringLiteral("Upgrade"), QString());
888 const QString connection = headers.value(QStringLiteral("Connection"), QString());
889 // unused for the moment
890 // const QString extensions = headers.value(QStringLiteral("Sec-WebSocket-Extensions"),
892 // const QString protocol = headers.value(QStringLiteral("Sec-WebSocket-Protocol"),
894 const QString version = headers.value(QStringLiteral("Sec-WebSocket-Version"),
897 if (Q_LIKELY(httpStatusCode == 101)) {
898 //HTTP/x.y 101 Switching Protocols
899 bool conversionOk = false;
900 const float version = httpProtocol.midRef(5).toFloat(&conversionOk);
901 //TODO: do not check the httpStatusText right now
902 ok = !(acceptKey.isEmpty() ||
903 (!conversionOk || (version < 1.1f)) ||
904 (upgrade.toLower() != QStringLiteral("websocket")) ||
905 (connection.toLower() != QStringLiteral("upgrade")));
907 const QString accept = calculateAcceptKey(m_key);
908 ok = (accept == acceptKey);
911 QWebSocket::tr("Accept-Key received from server %1 does not match the client key %2.")
912 .arg(acceptKey).arg(accept);
915 QWebSocket::tr("QWebSocketPrivate::processHandshake: Invalid statusline in response: %1.")
918 } else if (httpStatusCode == 400) {
919 //HTTP/1.1 400 Bad Request
920 if (!version.isEmpty()) {
921 const QStringList versions = version.split(QStringLiteral(", "),
922 QString::SkipEmptyParts);
923 if (!versions.contains(QString::number(QWebSocketProtocol::currentVersion()))) {
924 //if needed to switch protocol version, then we are finished here
925 //because we cannot handle other protocols than the RFC one (v13)
927 QWebSocket::tr("Handshake: Server requests a version that we don't support: %1.")
928 .arg(versions.join(QStringLiteral(", ")));
931 //we tried v13, but something different went wrong
933 QWebSocket::tr("QWebSocketPrivate::processHandshake: Unknown error condition encountered. Aborting connection.");
939 QWebSocket::tr("QWebSocketPrivate::processHandshake: Unhandled http status code: %1 (%2).")
940 .arg(httpStatusCode).arg(httpStatusMessage);
945 setErrorString(errorDescription);
946 Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
948 //handshake succeeded
949 setSocketState(QAbstractSocket::ConnectedState);
950 Q_EMIT q->connected();
958 void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketState)
962 QAbstractSocket::SocketState webSocketState = this->state();
963 switch (socketState) {
964 case QAbstractSocket::ConnectedState:
965 if (webSocketState == QAbstractSocket::ConnectingState) {
966 m_key = generateKey();
967 const QString handshake =
968 createHandShakeRequest(m_resourceName,
970 % QStringLiteral(":")
971 % QString::number(m_requestUrl.port(80)),
976 m_pSocket->write(handshake.toLatin1());
980 case QAbstractSocket::ClosingState:
981 if (webSocketState == QAbstractSocket::ConnectedState)
982 setSocketState(QAbstractSocket::ClosingState);
985 case QAbstractSocket::UnconnectedState:
986 if (webSocketState != QAbstractSocket::UnconnectedState) {
987 setSocketState(QAbstractSocket::UnconnectedState);
988 Q_EMIT q->disconnected();
992 case QAbstractSocket::HostLookupState:
993 case QAbstractSocket::ConnectingState:
994 case QAbstractSocket::BoundState:
995 case QAbstractSocket::ListeningState:
997 //to make C++ compiler happy;
1007 void QWebSocketPrivate::processData()
1009 Q_ASSERT(m_pSocket);
1010 while (m_pSocket->bytesAvailable()) {
1011 if (state() == QAbstractSocket::ConnectingState)
1012 processHandshake(m_pSocket.data());
1014 m_dataProcessor.process(m_pSocket.data());
1021 void QWebSocketPrivate::processPing(const QByteArray &data)
1023 Q_ASSERT(m_pSocket);
1024 quint32 maskingKey = 0;
1026 maskingKey = generateMaskingKey();
1027 m_pSocket->write(getFrameHeader(QWebSocketProtocol::OpCodePong, data.size(), maskingKey, true));
1028 if (data.size() > 0) {
1029 QByteArray maskedData = data;
1031 QWebSocketProtocol::mask(&maskedData, maskingKey);
1032 m_pSocket->write(maskedData);
1039 void QWebSocketPrivate::processPong(const QByteArray &data)
1042 Q_EMIT q->pong(static_cast<quint64>(m_pingTimer.elapsed()), data);
1048 void QWebSocketPrivate::processClose(QWebSocketProtocol::CloseCode closeCode, QString closeReason)
1050 m_isClosingHandshakeReceived = true;
1051 close(closeCode, closeReason);
1057 QString QWebSocketPrivate::createHandShakeRequest(QString resourceName,
1064 QStringList handshakeRequest;
1066 handshakeRequest << QStringLiteral("GET ") % resourceName % QStringLiteral(" HTTP/1.1") <<
1067 QStringLiteral("Host: ") % host <<
1068 QStringLiteral("Upgrade: websocket") <<
1069 QStringLiteral("Connection: Upgrade") <<
1070 QStringLiteral("Sec-WebSocket-Key: ") % QString::fromLatin1(key);
1071 if (!origin.isEmpty())
1072 handshakeRequest << QStringLiteral("Origin: ") % origin;
1073 handshakeRequest << QStringLiteral("Sec-WebSocket-Version: ")
1074 % QString::number(QWebSocketProtocol::currentVersion());
1075 if (extensions.length() > 0)
1076 handshakeRequest << QStringLiteral("Sec-WebSocket-Extensions: ") % extensions;
1077 if (protocols.length() > 0)
1078 handshakeRequest << QStringLiteral("Sec-WebSocket-Protocol: ") % protocols;
1079 handshakeRequest << QStringLiteral("\r\n");
1081 return handshakeRequest.join(QStringLiteral("\r\n"));
1087 QAbstractSocket::SocketState QWebSocketPrivate::state() const
1089 return m_socketState;
1095 void QWebSocketPrivate::setSocketState(QAbstractSocket::SocketState state)
1098 if (m_socketState != state) {
1099 m_socketState = state;
1100 Q_EMIT q->stateChanged(m_socketState);
1107 void QWebSocketPrivate::setErrorString(const QString &errorString)
1109 if (m_errorString != errorString)
1110 m_errorString = errorString;
1116 QHostAddress QWebSocketPrivate::localAddress() const
1118 QHostAddress address;
1119 if (Q_LIKELY(m_pSocket))
1120 address = m_pSocket->localAddress();
1127 quint16 QWebSocketPrivate::localPort() const
1130 if (Q_LIKELY(m_pSocket))
1131 port = m_pSocket->localPort();
1138 QAbstractSocket::PauseModes QWebSocketPrivate::pauseMode() const
1146 QHostAddress QWebSocketPrivate::peerAddress() const
1148 QHostAddress address;
1149 if (Q_LIKELY(m_pSocket))
1150 address = m_pSocket->peerAddress();
1157 QString QWebSocketPrivate::peerName() const
1160 if (Q_LIKELY(m_pSocket))
1161 name = m_pSocket->peerName();
1168 quint16 QWebSocketPrivate::peerPort() const
1171 if (Q_LIKELY(m_pSocket))
1172 port = m_pSocket->peerPort();
1176 #ifndef QT_NO_NETWORKPROXY
1180 QNetworkProxy QWebSocketPrivate::proxy() const
1182 return m_configuration.m_proxy;
1188 void QWebSocketPrivate::setProxy(const QNetworkProxy &networkProxy)
1190 if (networkProxy != networkProxy)
1191 m_configuration.m_proxy = networkProxy;
1193 #endif //QT_NO_NETWORKPROXY
1198 qint64 QWebSocketPrivate::readBufferSize() const
1200 return m_readBufferSize;
1206 void QWebSocketPrivate::resume()
1208 if (Q_LIKELY(m_pSocket))
1209 m_pSocket->resume();
1215 void QWebSocketPrivate::setPauseMode(QAbstractSocket::PauseModes pauseMode)
1217 m_pauseMode = pauseMode;
1218 if (Q_LIKELY(m_pSocket))
1219 m_pSocket->setPauseMode(m_pauseMode);
1225 void QWebSocketPrivate::setReadBufferSize(qint64 size)
1227 m_readBufferSize = size;
1228 if (Q_LIKELY(m_pSocket))
1229 m_pSocket->setReadBufferSize(m_readBufferSize);
1235 bool QWebSocketPrivate::isValid() const
1237 return (m_pSocket && m_pSocket->isValid() &&
1238 (m_socketState == QAbstractSocket::ConnectedState));