1 /****************************************************************************
3 ** Copyright (C) 2014 Kurt Pattyn <pattyn.kurt@gmail.com>.
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"
47 #include "qdefaultmaskgenerator_p.h"
49 #include <QtCore/QUrl>
50 #include <QtNetwork/QAuthenticator>
51 #include <QtNetwork/QTcpSocket>
52 #include <QtCore/QByteArray>
53 #include <QtCore/QtEndian>
54 #include <QtCore/QCryptographicHash>
55 #include <QtCore/QRegularExpression>
56 #include <QtCore/QStringList>
57 #include <QtNetwork/QHostAddress>
58 #include <QtCore/QStringBuilder> //for more efficient string concatenation
59 #ifndef QT_NONETWORKPROXY
60 #include <QtNetwork/QNetworkProxy>
63 #include <QtNetwork/QSslConfiguration>
64 #include <QtNetwork/QSslError>
67 #include <QtCore/QDebug>
73 const quint64 FRAME_SIZE_IN_BYTES = 512 * 512 * 2; //maximum size of a frame when sending a message
75 QWebSocketConfiguration::QWebSocketConfiguration() :
77 m_sslConfiguration(QSslConfiguration::defaultConfiguration()),
79 m_ignoreSslErrors(false),
81 #ifndef QT_NO_NETWORKPROXY
82 m_proxy(QNetworkProxy::DefaultProxy),
91 QWebSocketPrivate::QWebSocketPrivate(const QString &origin, QWebSocketProtocol::Version version,
92 QWebSocket *pWebSocket) :
103 m_socketState(QAbstractSocket::UnconnectedState),
104 m_pauseMode(QAbstractSocket::PauseNever),
108 m_isClosingHandshakeSent(false),
109 m_isClosingHandshakeReceived(false),
110 m_closeCode(QWebSocketProtocol::CloseCodeNormal),
115 m_pMaskGenerator(&m_defaultMaskGenerator),
116 m_defaultMaskGenerator()
123 QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version,
124 QWebSocket *pWebSocket) :
127 m_pSocket(pTcpSocket),
128 m_errorString(pTcpSocket->errorString()),
135 m_socketState(pTcpSocket->state()),
136 m_pauseMode(pTcpSocket->pauseMode()),
137 m_readBufferSize(pTcpSocket->readBufferSize()),
140 m_isClosingHandshakeSent(false),
141 m_isClosingHandshakeReceived(false),
142 m_closeCode(QWebSocketProtocol::CloseCodeNormal),
147 m_pMaskGenerator(&m_defaultMaskGenerator),
148 m_defaultMaskGenerator()
155 void QWebSocketPrivate::init()
158 Q_ASSERT(m_pMaskGenerator);
160 m_pMaskGenerator->seed();
163 makeConnections(m_pSocket.data());
170 QWebSocketPrivate::~QWebSocketPrivate()
174 if (state() == QAbstractSocket::ConnectedState)
175 close(QWebSocketProtocol::CloseCodeGoingAway, QWebSocket::tr("Connection closed"));
176 releaseConnections(m_pSocket.data());
182 void QWebSocketPrivate::abort()
191 QAbstractSocket::SocketError QWebSocketPrivate::error() const
193 QAbstractSocket::SocketError err = QAbstractSocket::UnknownSocketError;
194 if (Q_LIKELY(m_pSocket))
195 err = m_pSocket->error();
202 QString QWebSocketPrivate::errorString() const
205 if (!m_errorString.isEmpty())
206 errMsg = m_errorString;
208 errMsg = m_pSocket->errorString();
215 bool QWebSocketPrivate::flush()
218 if (Q_LIKELY(m_pSocket))
219 result = m_pSocket->flush();
226 qint64 QWebSocketPrivate::sendTextMessage(const QString &message)
228 return doWriteFrames(message.toUtf8(), false);
234 qint64 QWebSocketPrivate::sendBinaryMessage(const QByteArray &data)
236 return doWriteFrames(data, true);
243 void QWebSocketPrivate::setSslConfiguration(const QSslConfiguration &sslConfiguration)
245 m_configuration.m_sslConfiguration = sslConfiguration;
251 QSslConfiguration QWebSocketPrivate::sslConfiguration() const
253 return m_configuration.m_sslConfiguration;
259 void QWebSocketPrivate::ignoreSslErrors(const QList<QSslError> &errors)
261 m_configuration.m_ignoredSslErrors = errors;
267 void QWebSocketPrivate::ignoreSslErrors()
269 m_configuration.m_ignoreSslErrors = true;
270 if (Q_LIKELY(m_pSocket)) {
271 QSslSocket *pSslSocket = qobject_cast<QSslSocket *>(m_pSocket.data());
272 if (Q_LIKELY(pSslSocket))
273 pSslSocket->ignoreSslErrors();
280 Called from QWebSocketServer
283 QWebSocket *QWebSocketPrivate::upgradeFrom(QTcpSocket *pTcpSocket,
284 const QWebSocketHandshakeRequest &request,
285 const QWebSocketHandshakeResponse &response,
288 QWebSocket *pWebSocket = new QWebSocket(pTcpSocket, response.acceptedVersion(), parent);
289 if (Q_LIKELY(pWebSocket)) {
290 pWebSocket->d_func()->setExtension(response.acceptedExtension());
291 pWebSocket->d_func()->setOrigin(request.origin());
292 pWebSocket->d_func()->setRequestUrl(request.requestUrl());
293 pWebSocket->d_func()->setProtocol(response.acceptedProtocol());
294 pWebSocket->d_func()->setResourceName(request.requestUrl().toString(QUrl::RemoveUserInfo));
295 //a server should not send masked frames
296 pWebSocket->d_func()->enableMasking(false);
305 void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString reason)
307 if (Q_UNLIKELY(!m_pSocket))
309 if (!m_isClosingHandshakeSent) {
311 const quint16 code = qToBigEndian<quint16>(closeCode);
313 payload.append(static_cast<const char *>(static_cast<const void *>(&code)), 2);
314 if (!reason.isEmpty())
315 payload.append(reason.toUtf8());
316 quint32 maskingKey = 0;
318 maskingKey = generateMaskingKey();
319 QWebSocketProtocol::mask(payload.data(), payload.size(), maskingKey);
321 QByteArray frame = getFrameHeader(QWebSocketProtocol::OpCodeClose,
322 payload.size(), maskingKey, true);
323 frame.append(payload);
324 m_pSocket->write(frame);
327 m_isClosingHandshakeSent = true;
329 Q_EMIT q->aboutToClose();
337 void QWebSocketPrivate::open(const QUrl &url, bool mask)
339 //just delete the old socket for the moment;
340 //later, we can add more 'intelligent' handling by looking at the URL
343 if (!url.isValid() || url.toString().contains(QStringLiteral("\r\n"))) {
344 setErrorString(QWebSocket::tr("Invalid URL."));
345 Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
348 QTcpSocket *pTcpSocket = m_pSocket.take();
350 releaseConnections(pTcpSocket);
351 pTcpSocket->deleteLater();
354 if (Q_LIKELY(!m_pSocket)) {
355 m_dataProcessor.clear();
356 m_isClosingHandshakeReceived = false;
357 m_isClosingHandshakeSent = false;
360 QString resourceName = url.path();
361 if (resourceName.contains(QStringLiteral("\r\n"))) {
362 setRequestUrl(QUrl()); //clear requestUrl
363 setErrorString(QWebSocket::tr("Invalid resource name."));
364 Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
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 =
382 QWebSocket::tr("SSL Sockets are not supported on this platform.");
383 setErrorString(message);
384 Q_EMIT q->error(QAbstractSocket::UnsupportedSocketOperationError);
386 QSslSocket *sslSocket = new QSslSocket;
387 m_pSocket.reset(sslSocket);
388 if (Q_LIKELY(m_pSocket)) {
389 m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
390 m_pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
391 m_pSocket->setReadBufferSize(m_readBufferSize);
392 m_pSocket->setPauseMode(m_pauseMode);
394 makeConnections(m_pSocket.data());
395 QObject::connect(sslSocket, &QSslSocket::encryptedBytesWritten, q,
396 &QWebSocket::bytesWritten);
397 typedef void (QSslSocket:: *sslErrorSignalType)(const QList<QSslError> &);
398 QObject::connect(sslSocket,
399 static_cast<sslErrorSignalType>(&QSslSocket::sslErrors),
400 q, &QWebSocket::sslErrors);
401 setSocketState(QAbstractSocket::ConnectingState);
403 sslSocket->setSslConfiguration(m_configuration.m_sslConfiguration);
404 if (Q_UNLIKELY(m_configuration.m_ignoreSslErrors))
405 sslSocket->ignoreSslErrors();
407 sslSocket->ignoreSslErrors(m_configuration.m_ignoredSslErrors);
408 #ifndef QT_NO_NETWORKPROXY
409 sslSocket->setProxy(m_configuration.m_proxy);
411 sslSocket->connectToHostEncrypted(url.host(), url.port(443));
413 const QString message = QWebSocket::tr("Out of memory.");
414 setErrorString(message);
415 Q_EMIT q->error(QAbstractSocket::SocketResourceError);
420 if (url.scheme() == QStringLiteral("ws")) {
421 m_pSocket.reset(new QTcpSocket);
422 if (Q_LIKELY(m_pSocket)) {
423 m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
424 m_pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
425 m_pSocket->setReadBufferSize(m_readBufferSize);
426 m_pSocket->setPauseMode(m_pauseMode);
428 makeConnections(m_pSocket.data());
429 QObject::connect(m_pSocket.data(), &QAbstractSocket::bytesWritten, q,
430 &QWebSocket::bytesWritten);
431 setSocketState(QAbstractSocket::ConnectingState);
432 #ifndef QT_NO_NETWORKPROXY
433 m_pSocket->setProxy(m_configuration.m_proxy);
435 m_pSocket->connectToHost(url.host(), url.port(80));
437 const QString message = QWebSocket::tr("Out of memory.");
438 setErrorString(message);
439 Q_EMIT q->error(QAbstractSocket::SocketResourceError);
442 const QString message =
443 QWebSocket::tr("Unsupported WebSocket scheme: %1").arg(url.scheme());
444 setErrorString(message);
445 Q_EMIT q->error(QAbstractSocket::UnsupportedSocketOperationError);
453 void QWebSocketPrivate::ping(const QByteArray &payload)
455 QByteArray payloadTruncated = payload.left(125);
456 m_pingTimer.restart();
457 QByteArray pingFrame = getFrameHeader(QWebSocketProtocol::OpCodePing, payloadTruncated.size(),
458 0 /*do not mask*/, true);
459 pingFrame.append(payloadTruncated);
460 qint64 ret = writeFrame(pingFrame);
466 Sets the version to use for the WebSocket protocol;
467 this must be set before the socket is opened.
469 void QWebSocketPrivate::setVersion(QWebSocketProtocol::Version version)
471 if (m_version != version)
477 Sets the resource name of the connection; must be set before the socket is openend
479 void QWebSocketPrivate::setResourceName(const QString &resourceName)
481 if (m_resourceName != resourceName)
482 m_resourceName = resourceName;
488 void QWebSocketPrivate::setRequestUrl(const QUrl &requestUrl)
490 if (m_requestUrl != requestUrl)
491 m_requestUrl = requestUrl;
497 void QWebSocketPrivate::setOrigin(const QString &origin)
499 if (m_origin != origin)
506 void QWebSocketPrivate::setProtocol(const QString &protocol)
508 if (m_protocol != protocol)
509 m_protocol = protocol;
515 void QWebSocketPrivate::setExtension(const QString &extension)
517 if (m_extension != extension)
518 m_extension = extension;
524 void QWebSocketPrivate::enableMasking(bool enable)
526 if (m_mustMask != enable)
533 void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket)
535 Q_ASSERT(pTcpSocket);
538 if (Q_LIKELY(pTcpSocket)) {
539 //pass through signals
540 typedef void (QAbstractSocket:: *ASErrorSignal)(QAbstractSocket::SocketError);
541 typedef void (QWebSocket:: *WSErrorSignal)(QAbstractSocket::SocketError);
542 QObject::connect(pTcpSocket,
543 static_cast<ASErrorSignal>(&QAbstractSocket::error),
544 q, static_cast<WSErrorSignal>(&QWebSocket::error));
545 #ifndef QT_NO_NETWORKPROXY
546 QObject::connect(pTcpSocket, &QAbstractSocket::proxyAuthenticationRequired, q,
547 &QWebSocket::proxyAuthenticationRequired);
549 QObject::connect(pTcpSocket, &QAbstractSocket::readChannelFinished, q,
550 &QWebSocket::readChannelFinished);
551 QObject::connect(pTcpSocket, &QAbstractSocket::aboutToClose, q, &QWebSocket::aboutToClose);
554 QObjectPrivate::connect(pTcpSocket, &QAbstractSocket::stateChanged, this,
555 &QWebSocketPrivate::processStateChanged);
556 //!!!important to use a QueuedConnection here;
557 //with QTcpSocket there is no problem, but with QSslSocket the processing hangs
558 QObjectPrivate::connect(pTcpSocket, &QAbstractSocket::readyRead, this,
559 &QWebSocketPrivate::processData, Qt::QueuedConnection);
562 QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::textFrameReceived, q,
563 &QWebSocket::textFrameReceived);
564 QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryFrameReceived, q,
565 &QWebSocket::binaryFrameReceived);
566 QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryMessageReceived, q,
567 &QWebSocket::binaryMessageReceived);
568 QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::textMessageReceived, q,
569 &QWebSocket::textMessageReceived);
570 QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::errorEncountered, this,
571 &QWebSocketPrivate::close);
572 QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::pingReceived, this,
573 &QWebSocketPrivate::processPing);
574 QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::pongReceived, this,
575 &QWebSocketPrivate::processPong);
576 QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::closeReceived, this,
577 &QWebSocketPrivate::processClose);
583 void QWebSocketPrivate::releaseConnections(const QTcpSocket *pTcpSocket)
585 if (Q_LIKELY(pTcpSocket))
586 pTcpSocket->disconnect(pTcpSocket);
587 m_dataProcessor.disconnect();
593 QWebSocketProtocol::Version QWebSocketPrivate::version() const
601 QString QWebSocketPrivate::resourceName() const
603 return m_resourceName;
609 QUrl QWebSocketPrivate::requestUrl() const
617 QString QWebSocketPrivate::origin() const
625 QString QWebSocketPrivate::protocol() const
633 QString QWebSocketPrivate::extension() const
641 QWebSocketProtocol::CloseCode QWebSocketPrivate::closeCode() const
649 QString QWebSocketPrivate::closeReason() const
651 return m_closeReason;
657 QByteArray QWebSocketPrivate::getFrameHeader(QWebSocketProtocol::OpCode opCode,
658 quint64 payloadLength, quint32 maskingKey,
663 bool ok = payloadLength <= 0x7FFFFFFFFFFFFFFFULL;
666 //FIN, RSV1-3, opcode (RSV-1, RSV-2 and RSV-3 are zero)
667 byte = static_cast<quint8>((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00));
668 header.append(static_cast<char>(byte));
673 if (payloadLength <= 125) {
674 byte |= static_cast<quint8>(payloadLength);
675 header.append(static_cast<char>(byte));
676 } else if (payloadLength <= 0xFFFFU) {
678 header.append(static_cast<char>(byte));
679 quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
680 header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
681 } else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL) {
683 header.append(static_cast<char>(byte));
684 quint64 swapped = qToBigEndian<quint64>(payloadLength);
685 header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8);
688 if (maskingKey != 0) {
689 const quint32 mask = qToBigEndian<quint32>(maskingKey);
690 header.append(static_cast<const char *>(static_cast<const void *>(&mask)),
694 setErrorString(QStringLiteral("WebSocket::getHeader: payload too big!"));
695 Q_EMIT q_ptr->error(QAbstractSocket::DatagramTooLargeError);
704 qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary)
706 qint64 payloadWritten = 0;
707 if (Q_UNLIKELY(!m_pSocket) || (state() != QAbstractSocket::ConnectedState))
708 return payloadWritten;
711 const QWebSocketProtocol::OpCode firstOpCode = isBinary ?
712 QWebSocketProtocol::OpCodeBinary : QWebSocketProtocol::OpCodeText;
714 int numFrames = data.size() / FRAME_SIZE_IN_BYTES;
715 QByteArray tmpData(data);
717 char *payload = tmpData.data();
718 quint64 sizeLeft = quint64(data.size()) % FRAME_SIZE_IN_BYTES;
719 if (Q_LIKELY(sizeLeft))
722 //catch the case where the payload is zero bytes;
723 //in this case, we still need to send a frame
724 if (Q_UNLIKELY(numFrames == 0))
726 quint64 currentPosition = 0;
727 qint64 bytesWritten = 0;
728 quint64 bytesLeft = data.size();
730 for (int i = 0; i < numFrames; ++i) {
731 quint32 maskingKey = 0;
733 maskingKey = generateMaskingKey();
735 const bool isLastFrame = (i == (numFrames - 1));
736 const bool isFirstFrame = (i == 0);
738 const quint64 size = qMin(bytesLeft, FRAME_SIZE_IN_BYTES);
739 const QWebSocketProtocol::OpCode opcode = isFirstFrame ? firstOpCode
740 : QWebSocketProtocol::OpCodeContinue;
743 bytesWritten += m_pSocket->write(getFrameHeader(opcode, size, maskingKey, isLastFrame));
746 if (Q_LIKELY(size > 0)) {
747 char *currentData = payload + currentPosition;
749 QWebSocketProtocol::mask(currentData, size, maskingKey);
750 qint64 written = m_pSocket->write(currentData, static_cast<qint64>(size));
751 if (Q_LIKELY(written > 0)) {
752 bytesWritten += written;
753 payloadWritten += written;
756 setErrorString(QWebSocket::tr("Error writing bytes to socket: %1.")
757 .arg(m_pSocket->errorString()));
758 Q_EMIT q->error(QAbstractSocket::NetworkError);
762 currentPosition += size;
765 if (Q_UNLIKELY(payloadWritten != data.size())) {
766 setErrorString(QWebSocket::tr("Bytes written %1 != %2.")
767 .arg(payloadWritten).arg(data.size()));
768 Q_EMIT q->error(QAbstractSocket::NetworkError);
770 return payloadWritten;
776 quint32 QWebSocketPrivate::generateMaskingKey() const
778 return m_pMaskGenerator->nextMask();
784 QByteArray QWebSocketPrivate::generateKey() const
788 for (int i = 0; i < 4; ++i) {
789 const quint32 tmp = m_pMaskGenerator->nextMask();
790 key.append(static_cast<const char *>(static_cast<const void *>(&tmp)), sizeof(quint32));
793 return key.toBase64();
800 QString QWebSocketPrivate::calculateAcceptKey(const QByteArray &key) const
802 const QByteArray tmpKey = key + QByteArrayLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
803 const QByteArray hash = QCryptographicHash::hash(tmpKey, QCryptographicHash::Sha1).toBase64();
804 return QString::fromLatin1(hash);
810 qint64 QWebSocketPrivate::writeFrames(const QList<QByteArray> &frames)
813 if (Q_LIKELY(m_pSocket)) {
814 QList<QByteArray>::const_iterator it;
815 for (it = frames.cbegin(); it < frames.cend(); ++it)
816 written += writeFrame(*it);
824 qint64 QWebSocketPrivate::writeFrame(const QByteArray &frame)
827 if (Q_LIKELY(m_pSocket))
828 written = m_pSocket->write(frame);
835 QString readLine(QTcpSocket *pSocket)
840 while (pSocket->getChar(&c)) {
841 if (c == char('\r')) {
842 pSocket->getChar(&c);
845 line.append(QChar::fromLatin1(c));
851 // this function is a copy of QHttpNetworkReplyPrivate::parseStatus
852 static bool parseStatusLine(const QByteArray &status, int *majorVersion, int *minorVersion,
853 int *statusCode, QString *reasonPhrase)
856 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
857 // HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
858 // that makes: 'HTTP/n.n xxx Message'
859 // byte count: 0123456789012
861 static const int minLength = 11;
862 static const int dotPos = 6;
863 static const int spacePos = 8;
864 static const char httpMagic[] = "HTTP/";
866 if (status.length() < minLength
867 || !status.startsWith(httpMagic)
868 || status.at(dotPos) != '.'
869 || status.at(spacePos) != ' ') {
870 // I don't know how to parse this status line
874 // optimize for the valid case: defer checking until the end
875 *majorVersion = status.at(dotPos - 1) - '0';
876 *minorVersion = status.at(dotPos + 1) - '0';
879 int j = status.indexOf(' ', i + 1); // j == -1 || at(j) == ' ' so j+1 == 0 && j+1 <= length()
880 const QByteArray code = status.mid(i + 1, j - i - 1);
883 *statusCode = code.toInt(&ok);
884 *reasonPhrase = QString::fromLatin1(status.constData() + j + 1);
886 return ok && uint(*majorVersion) <= 9 && uint(* minorVersion) <= 9;
890 //called on the client for a server handshake response
894 void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket)
897 if (Q_UNLIKELY(!pSocket))
901 QString errorDescription;
903 const QByteArray statusLine = pSocket->readLine();
904 int httpMajorVersion, httpMinorVersion;
906 QString httpStatusMessage;
907 if (Q_UNLIKELY(!parseStatusLine(statusLine, &httpMajorVersion, &httpMinorVersion,
908 &httpStatusCode, &httpStatusMessage))) {
909 errorDescription = QWebSocket::tr("Invalid statusline in response: %1.").arg(QString::fromLatin1(statusLine));
911 QString headerLine = readLine(pSocket);
912 QMap<QString, QString> headers;
913 while (!headerLine.isEmpty()) {
914 const QStringList headerField = headerLine.split(QStringLiteral(": "),
915 QString::SkipEmptyParts);
916 if (headerField.size() == 2) {
917 headers.insertMulti(headerField[0].toLower(), headerField[1]);
919 headerLine = readLine(pSocket);
922 const QString acceptKey = headers.value(QStringLiteral("sec-websocket-accept"),
924 const QString upgrade = headers.value(QStringLiteral("upgrade"), QString());
925 const QString connection = headers.value(QStringLiteral("connection"), QString());
926 // unused for the moment
927 // const QString extensions = headers.value(QStringLiteral("sec-websocket-extensions"),
929 // const QString protocol = headers.value(QStringLiteral("sec-websocket-protocol"),
931 const QString version = headers.value(QStringLiteral("sec-websocket-version"),
934 if (Q_LIKELY(httpStatusCode == 101)) {
935 //HTTP/x.y 101 Switching Protocols
936 //TODO: do not check the httpStatusText right now
937 ok = !(acceptKey.isEmpty() ||
938 (httpMajorVersion < 1 || httpMinorVersion < 1) ||
939 (upgrade.toLower() != QStringLiteral("websocket")) ||
940 (connection.toLower() != QStringLiteral("upgrade")));
942 const QString accept = calculateAcceptKey(m_key);
943 ok = (accept == acceptKey);
946 QWebSocket::tr("Accept-Key received from server %1 does not match the client key %2.")
947 .arg(acceptKey).arg(accept);
950 QWebSocket::tr("QWebSocketPrivate::processHandshake: Invalid statusline in response: %1.")
951 .arg(QString::fromLatin1(statusLine));
953 } else if (httpStatusCode == 400) {
954 //HTTP/1.1 400 Bad Request
955 if (!version.isEmpty()) {
956 const QStringList versions = version.split(QStringLiteral(", "),
957 QString::SkipEmptyParts);
958 if (!versions.contains(QString::number(QWebSocketProtocol::currentVersion()))) {
959 //if needed to switch protocol version, then we are finished here
960 //because we cannot handle other protocols than the RFC one (v13)
962 QWebSocket::tr("Handshake: Server requests a version that we don't support: %1.")
963 .arg(versions.join(QStringLiteral(", ")));
966 //we tried v13, but something different went wrong
968 QWebSocket::tr("QWebSocketPrivate::processHandshake: Unknown error condition encountered. Aborting connection.");
974 QWebSocket::tr("QWebSocketPrivate::processHandshake: Unhandled http status code: %1 (%2).")
975 .arg(httpStatusCode).arg(httpStatusMessage);
980 setErrorString(errorDescription);
981 Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
983 //handshake succeeded
984 setSocketState(QAbstractSocket::ConnectedState);
985 Q_EMIT q->connected();
993 void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketState)
997 QAbstractSocket::SocketState webSocketState = this->state();
998 switch (socketState) {
999 case QAbstractSocket::ConnectedState:
1000 if (webSocketState == QAbstractSocket::ConnectingState) {
1001 m_key = generateKey();
1002 const QString handshake =
1003 createHandShakeRequest(m_resourceName,
1005 % QStringLiteral(":")
1006 % QString::number(m_requestUrl.port(80)),
1011 if (handshake.isEmpty()) {
1013 Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
1016 m_pSocket->write(handshake.toLatin1());
1020 case QAbstractSocket::ClosingState:
1021 if (webSocketState == QAbstractSocket::ConnectedState)
1022 setSocketState(QAbstractSocket::ClosingState);
1025 case QAbstractSocket::UnconnectedState:
1026 if (webSocketState != QAbstractSocket::UnconnectedState) {
1027 setSocketState(QAbstractSocket::UnconnectedState);
1028 Q_EMIT q->disconnected();
1032 case QAbstractSocket::HostLookupState:
1033 case QAbstractSocket::ConnectingState:
1034 case QAbstractSocket::BoundState:
1035 case QAbstractSocket::ListeningState:
1037 //to make C++ compiler happy;
1047 void QWebSocketPrivate::processData()
1049 Q_ASSERT(m_pSocket);
1050 while (m_pSocket->bytesAvailable()) {
1051 if (state() == QAbstractSocket::ConnectingState)
1052 processHandshake(m_pSocket.data());
1054 m_dataProcessor.process(m_pSocket.data());
1061 void QWebSocketPrivate::processPing(const QByteArray &data)
1063 Q_ASSERT(m_pSocket);
1064 quint32 maskingKey = 0;
1066 maskingKey = generateMaskingKey();
1067 m_pSocket->write(getFrameHeader(QWebSocketProtocol::OpCodePong, data.size(), maskingKey, true));
1068 if (data.size() > 0) {
1069 QByteArray maskedData = data;
1071 QWebSocketProtocol::mask(&maskedData, maskingKey);
1072 m_pSocket->write(maskedData);
1079 void QWebSocketPrivate::processPong(const QByteArray &data)
1082 Q_EMIT q->pong(static_cast<quint64>(m_pingTimer.elapsed()), data);
1088 void QWebSocketPrivate::processClose(QWebSocketProtocol::CloseCode closeCode, QString closeReason)
1090 m_isClosingHandshakeReceived = true;
1091 close(closeCode, closeReason);
1097 QString QWebSocketPrivate::createHandShakeRequest(QString resourceName,
1104 QStringList handshakeRequest;
1105 if (resourceName.contains(QStringLiteral("\r\n"))) {
1106 setErrorString(QWebSocket::tr("The resource name contains newlines. " \
1107 "Possible attack detected."));
1110 if (host.contains(QStringLiteral("\r\n"))) {
1111 setErrorString(QWebSocket::tr("The hostname contains newlines. " \
1112 "Possible attack detected."));
1115 if (origin.contains(QStringLiteral("\r\n"))) {
1116 setErrorString(QWebSocket::tr("The origin contains newlines. " \
1117 "Possible attack detected."));
1120 if (extensions.contains(QStringLiteral("\r\n"))) {
1121 setErrorString(QWebSocket::tr("The extensions attribute contains newlines. " \
1122 "Possible attack detected."));
1125 if (protocols.contains(QStringLiteral("\r\n"))) {
1126 setErrorString(QWebSocket::tr("The protocols attribute contains newlines. " \
1127 "Possible attack detected."));
1131 handshakeRequest << QStringLiteral("GET ") % resourceName % QStringLiteral(" HTTP/1.1") <<
1132 QStringLiteral("Host: ") % host <<
1133 QStringLiteral("Upgrade: websocket") <<
1134 QStringLiteral("Connection: Upgrade") <<
1135 QStringLiteral("Sec-WebSocket-Key: ") % QString::fromLatin1(key);
1136 if (!origin.isEmpty())
1137 handshakeRequest << QStringLiteral("Origin: ") % origin;
1138 handshakeRequest << QStringLiteral("Sec-WebSocket-Version: ")
1139 % QString::number(QWebSocketProtocol::currentVersion());
1140 if (extensions.length() > 0)
1141 handshakeRequest << QStringLiteral("Sec-WebSocket-Extensions: ") % extensions;
1142 if (protocols.length() > 0)
1143 handshakeRequest << QStringLiteral("Sec-WebSocket-Protocol: ") % protocols;
1144 handshakeRequest << QStringLiteral("\r\n");
1146 return handshakeRequest.join(QStringLiteral("\r\n"));
1152 QAbstractSocket::SocketState QWebSocketPrivate::state() const
1154 return m_socketState;
1160 void QWebSocketPrivate::setSocketState(QAbstractSocket::SocketState state)
1163 if (m_socketState != state) {
1164 m_socketState = state;
1165 Q_EMIT q->stateChanged(m_socketState);
1172 void QWebSocketPrivate::setErrorString(const QString &errorString)
1174 if (m_errorString != errorString)
1175 m_errorString = errorString;
1181 QHostAddress QWebSocketPrivate::localAddress() const
1183 QHostAddress address;
1184 if (Q_LIKELY(m_pSocket))
1185 address = m_pSocket->localAddress();
1192 quint16 QWebSocketPrivate::localPort() const
1195 if (Q_LIKELY(m_pSocket))
1196 port = m_pSocket->localPort();
1203 QAbstractSocket::PauseModes QWebSocketPrivate::pauseMode() const
1211 QHostAddress QWebSocketPrivate::peerAddress() const
1213 QHostAddress address;
1214 if (Q_LIKELY(m_pSocket))
1215 address = m_pSocket->peerAddress();
1222 QString QWebSocketPrivate::peerName() const
1225 if (Q_LIKELY(m_pSocket))
1226 name = m_pSocket->peerName();
1233 quint16 QWebSocketPrivate::peerPort() const
1236 if (Q_LIKELY(m_pSocket))
1237 port = m_pSocket->peerPort();
1241 #ifndef QT_NO_NETWORKPROXY
1245 QNetworkProxy QWebSocketPrivate::proxy() const
1247 return m_configuration.m_proxy;
1253 void QWebSocketPrivate::setProxy(const QNetworkProxy &networkProxy)
1255 if (m_configuration.m_proxy != networkProxy)
1256 m_configuration.m_proxy = networkProxy;
1258 #endif //QT_NO_NETWORKPROXY
1263 void QWebSocketPrivate::setMaskGenerator(const QMaskGenerator *maskGenerator)
1266 m_pMaskGenerator = &m_defaultMaskGenerator;
1267 else if (maskGenerator != m_pMaskGenerator)
1268 m_pMaskGenerator = const_cast<QMaskGenerator *>(maskGenerator);
1274 const QMaskGenerator *QWebSocketPrivate::maskGenerator() const
1276 Q_ASSERT(m_pMaskGenerator);
1277 return m_pMaskGenerator;
1283 qint64 QWebSocketPrivate::readBufferSize() const
1285 return m_readBufferSize;
1291 void QWebSocketPrivate::resume()
1293 if (Q_LIKELY(m_pSocket))
1294 m_pSocket->resume();
1300 void QWebSocketPrivate::setPauseMode(QAbstractSocket::PauseModes pauseMode)
1302 m_pauseMode = pauseMode;
1303 if (Q_LIKELY(m_pSocket))
1304 m_pSocket->setPauseMode(m_pauseMode);
1310 void QWebSocketPrivate::setReadBufferSize(qint64 size)
1312 m_readBufferSize = size;
1313 if (Q_LIKELY(m_pSocket))
1314 m_pSocket->setReadBufferSize(m_readBufferSize);
1320 bool QWebSocketPrivate::isValid() const
1322 return (m_pSocket && m_pSocket->isValid() &&
1323 (m_socketState == QAbstractSocket::ConnectedState));