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:LGPL21$
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 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
26 ** In addition, as a special exception, Digia gives you certain additional
27 ** rights. These rights are described in the Digia Qt LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
32 ****************************************************************************/
34 #include "qwebsocket.h"
35 #include "qwebsocket_p.h"
36 #include "qwebsocketprotocol_p.h"
37 #include "qwebsockethandshakerequest_p.h"
38 #include "qwebsockethandshakeresponse_p.h"
39 #include "qdefaultmaskgenerator_p.h"
41 #include <QtCore/QUrl>
42 #include <QtNetwork/QAuthenticator>
43 #include <QtNetwork/QTcpSocket>
44 #include <QtCore/QByteArray>
45 #include <QtCore/QtEndian>
46 #include <QtCore/QCryptographicHash>
47 #include <QtCore/QRegularExpression>
48 #include <QtCore/QStringList>
49 #include <QtNetwork/QHostAddress>
50 #include <QtCore/QStringBuilder> //for more efficient string concatenation
51 #ifndef QT_NONETWORKPROXY
52 #include <QtNetwork/QNetworkProxy>
55 #include <QtNetwork/QSslConfiguration>
56 #include <QtNetwork/QSslError>
59 #include <QtCore/QDebug>
65 const quint64 FRAME_SIZE_IN_BYTES = 512 * 512 * 2; //maximum size of a frame when sending a message
67 QWebSocketConfiguration::QWebSocketConfiguration() :
69 m_sslConfiguration(QSslConfiguration::defaultConfiguration()),
71 m_ignoreSslErrors(false),
73 #ifndef QT_NO_NETWORKPROXY
74 m_proxy(QNetworkProxy::DefaultProxy),
83 QWebSocketPrivate::QWebSocketPrivate(const QString &origin, QWebSocketProtocol::Version version,
84 QWebSocket *pWebSocket) :
95 m_socketState(QAbstractSocket::UnconnectedState),
96 m_pauseMode(QAbstractSocket::PauseNever),
100 m_isClosingHandshakeSent(false),
101 m_isClosingHandshakeReceived(false),
102 m_closeCode(QWebSocketProtocol::CloseCodeNormal),
107 m_pMaskGenerator(&m_defaultMaskGenerator),
108 m_defaultMaskGenerator()
115 QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version,
116 QWebSocket *pWebSocket) :
119 m_pSocket(pTcpSocket),
120 m_errorString(pTcpSocket->errorString()),
127 m_socketState(pTcpSocket->state()),
128 m_pauseMode(pTcpSocket->pauseMode()),
129 m_readBufferSize(pTcpSocket->readBufferSize()),
132 m_isClosingHandshakeSent(false),
133 m_isClosingHandshakeReceived(false),
134 m_closeCode(QWebSocketProtocol::CloseCodeNormal),
139 m_pMaskGenerator(&m_defaultMaskGenerator),
140 m_defaultMaskGenerator()
147 void QWebSocketPrivate::init()
150 Q_ASSERT(m_pMaskGenerator);
152 m_pMaskGenerator->seed();
155 makeConnections(m_pSocket.data());
162 QWebSocketPrivate::~QWebSocketPrivate()
166 if (state() == QAbstractSocket::ConnectedState)
167 close(QWebSocketProtocol::CloseCodeGoingAway, QWebSocket::tr("Connection closed"));
168 releaseConnections(m_pSocket.data());
174 void QWebSocketPrivate::abort()
183 QAbstractSocket::SocketError QWebSocketPrivate::error() const
185 QAbstractSocket::SocketError err = QAbstractSocket::UnknownSocketError;
186 if (Q_LIKELY(m_pSocket))
187 err = m_pSocket->error();
194 QString QWebSocketPrivate::errorString() const
197 if (!m_errorString.isEmpty())
198 errMsg = m_errorString;
200 errMsg = m_pSocket->errorString();
207 bool QWebSocketPrivate::flush()
210 if (Q_LIKELY(m_pSocket))
211 result = m_pSocket->flush();
218 qint64 QWebSocketPrivate::sendTextMessage(const QString &message)
220 return doWriteFrames(message.toUtf8(), false);
226 qint64 QWebSocketPrivate::sendBinaryMessage(const QByteArray &data)
228 return doWriteFrames(data, true);
235 void QWebSocketPrivate::setSslConfiguration(const QSslConfiguration &sslConfiguration)
237 m_configuration.m_sslConfiguration = sslConfiguration;
243 QSslConfiguration QWebSocketPrivate::sslConfiguration() const
245 return m_configuration.m_sslConfiguration;
251 void QWebSocketPrivate::ignoreSslErrors(const QList<QSslError> &errors)
253 m_configuration.m_ignoredSslErrors = errors;
259 void QWebSocketPrivate::ignoreSslErrors()
261 m_configuration.m_ignoreSslErrors = true;
262 if (Q_LIKELY(m_pSocket)) {
263 QSslSocket *pSslSocket = qobject_cast<QSslSocket *>(m_pSocket.data());
264 if (Q_LIKELY(pSslSocket))
265 pSslSocket->ignoreSslErrors();
272 Called from QWebSocketServer
275 QWebSocket *QWebSocketPrivate::upgradeFrom(QTcpSocket *pTcpSocket,
276 const QWebSocketHandshakeRequest &request,
277 const QWebSocketHandshakeResponse &response,
280 QWebSocket *pWebSocket = new QWebSocket(pTcpSocket, response.acceptedVersion(), parent);
281 if (Q_LIKELY(pWebSocket)) {
282 pWebSocket->d_func()->setExtension(response.acceptedExtension());
283 pWebSocket->d_func()->setOrigin(request.origin());
284 pWebSocket->d_func()->setRequestUrl(request.requestUrl());
285 pWebSocket->d_func()->setProtocol(response.acceptedProtocol());
286 pWebSocket->d_func()->setResourceName(request.requestUrl().toString(QUrl::RemoveUserInfo));
287 //a server should not send masked frames
288 pWebSocket->d_func()->enableMasking(false);
297 void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString reason)
299 if (Q_UNLIKELY(!m_pSocket))
301 if (!m_isClosingHandshakeSent) {
303 m_closeCode = closeCode;
304 m_closeReason = reason;
305 const quint16 code = qToBigEndian<quint16>(closeCode);
307 payload.append(static_cast<const char *>(static_cast<const void *>(&code)), 2);
308 if (!reason.isEmpty())
309 payload.append(reason.toUtf8());
310 quint32 maskingKey = 0;
312 maskingKey = generateMaskingKey();
313 QWebSocketProtocol::mask(payload.data(), payload.size(), maskingKey);
315 QByteArray frame = getFrameHeader(QWebSocketProtocol::OpCodeClose,
316 payload.size(), maskingKey, true);
317 frame.append(payload);
318 m_pSocket->write(frame);
321 m_isClosingHandshakeSent = true;
323 Q_EMIT q->aboutToClose();
331 void QWebSocketPrivate::open(const QUrl &url, bool mask)
333 //just delete the old socket for the moment;
334 //later, we can add more 'intelligent' handling by looking at the URL
337 if (!url.isValid() || url.toString().contains(QStringLiteral("\r\n"))) {
338 setErrorString(QWebSocket::tr("Invalid URL."));
339 Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
342 QTcpSocket *pTcpSocket = m_pSocket.take();
344 releaseConnections(pTcpSocket);
345 pTcpSocket->deleteLater();
348 if (Q_LIKELY(!m_pSocket)) {
349 m_dataProcessor.clear();
350 m_isClosingHandshakeReceived = false;
351 m_isClosingHandshakeSent = false;
354 QString resourceName = url.path();
355 if (resourceName.contains(QStringLiteral("\r\n"))) {
356 setRequestUrl(QUrl()); //clear requestUrl
357 setErrorString(QWebSocket::tr("Invalid resource name."));
358 Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
361 if (!url.query().isEmpty()) {
362 if (!resourceName.endsWith(QChar::fromLatin1('?'))) {
363 resourceName.append(QChar::fromLatin1('?'));
365 resourceName.append(url.query());
367 if (resourceName.isEmpty())
368 resourceName = QStringLiteral("/");
369 setResourceName(resourceName);
373 if (url.scheme() == QStringLiteral("wss")) {
374 if (!QSslSocket::supportsSsl()) {
375 const QString message =
376 QWebSocket::tr("SSL Sockets are not supported on this platform.");
377 setErrorString(message);
378 Q_EMIT q->error(QAbstractSocket::UnsupportedSocketOperationError);
380 QSslSocket *sslSocket = new QSslSocket;
381 m_pSocket.reset(sslSocket);
382 if (Q_LIKELY(m_pSocket)) {
383 m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
384 m_pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
385 m_pSocket->setReadBufferSize(m_readBufferSize);
386 m_pSocket->setPauseMode(m_pauseMode);
388 makeConnections(m_pSocket.data());
389 setSocketState(QAbstractSocket::ConnectingState);
391 sslSocket->setSslConfiguration(m_configuration.m_sslConfiguration);
392 if (Q_UNLIKELY(m_configuration.m_ignoreSslErrors))
393 sslSocket->ignoreSslErrors();
395 sslSocket->ignoreSslErrors(m_configuration.m_ignoredSslErrors);
396 #ifndef QT_NO_NETWORKPROXY
397 sslSocket->setProxy(m_configuration.m_proxy);
399 sslSocket->connectToHostEncrypted(url.host(), url.port(443));
401 const QString message = QWebSocket::tr("Out of memory.");
402 setErrorString(message);
403 Q_EMIT q->error(QAbstractSocket::SocketResourceError);
408 if (url.scheme() == QStringLiteral("ws")) {
409 m_pSocket.reset(new QTcpSocket);
410 if (Q_LIKELY(m_pSocket)) {
411 m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
412 m_pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
413 m_pSocket->setReadBufferSize(m_readBufferSize);
414 m_pSocket->setPauseMode(m_pauseMode);
416 makeConnections(m_pSocket.data());
417 setSocketState(QAbstractSocket::ConnectingState);
418 #ifndef QT_NO_NETWORKPROXY
419 m_pSocket->setProxy(m_configuration.m_proxy);
421 m_pSocket->connectToHost(url.host(), url.port(80));
423 const QString message = QWebSocket::tr("Out of memory.");
424 setErrorString(message);
425 Q_EMIT q->error(QAbstractSocket::SocketResourceError);
428 const QString message =
429 QWebSocket::tr("Unsupported WebSocket scheme: %1").arg(url.scheme());
430 setErrorString(message);
431 Q_EMIT q->error(QAbstractSocket::UnsupportedSocketOperationError);
439 void QWebSocketPrivate::ping(const QByteArray &payload)
441 QByteArray payloadTruncated = payload.left(125);
442 m_pingTimer.restart();
443 QByteArray pingFrame = getFrameHeader(QWebSocketProtocol::OpCodePing, payloadTruncated.size(),
444 0 /*do not mask*/, true);
445 pingFrame.append(payloadTruncated);
446 qint64 ret = writeFrame(pingFrame);
452 Sets the version to use for the WebSocket protocol;
453 this must be set before the socket is opened.
455 void QWebSocketPrivate::setVersion(QWebSocketProtocol::Version version)
457 if (m_version != version)
463 Sets the resource name of the connection; must be set before the socket is openend
465 void QWebSocketPrivate::setResourceName(const QString &resourceName)
467 if (m_resourceName != resourceName)
468 m_resourceName = resourceName;
474 void QWebSocketPrivate::setRequestUrl(const QUrl &requestUrl)
476 if (m_requestUrl != requestUrl)
477 m_requestUrl = requestUrl;
483 void QWebSocketPrivate::setOrigin(const QString &origin)
485 if (m_origin != origin)
492 void QWebSocketPrivate::setProtocol(const QString &protocol)
494 if (m_protocol != protocol)
495 m_protocol = protocol;
501 void QWebSocketPrivate::setExtension(const QString &extension)
503 if (m_extension != extension)
504 m_extension = extension;
510 void QWebSocketPrivate::enableMasking(bool enable)
512 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 typedef void (QAbstractSocket:: *ASErrorSignal)(QAbstractSocket::SocketError);
527 typedef void (QWebSocket:: *WSErrorSignal)(QAbstractSocket::SocketError);
528 QObject::connect(pTcpSocket,
529 static_cast<ASErrorSignal>(&QAbstractSocket::error),
530 q, static_cast<WSErrorSignal>(&QWebSocket::error));
531 #ifndef QT_NO_NETWORKPROXY
532 QObject::connect(pTcpSocket, &QAbstractSocket::proxyAuthenticationRequired, q,
533 &QWebSocket::proxyAuthenticationRequired);
534 #endif // QT_NO_NETWORKPROXY
535 QObject::connect(pTcpSocket, &QAbstractSocket::readChannelFinished, q,
536 &QWebSocket::readChannelFinished);
537 QObject::connect(pTcpSocket, &QAbstractSocket::aboutToClose, q, &QWebSocket::aboutToClose);
538 QObject::connect(pTcpSocket, &QAbstractSocket::bytesWritten, q, &QWebSocket::bytesWritten);
541 QObjectPrivate::connect(pTcpSocket, &QAbstractSocket::stateChanged, this,
542 &QWebSocketPrivate::processStateChanged);
543 //!!!important to use a QueuedConnection here;
544 //with QTcpSocket there is no problem, but with QSslSocket the processing hangs
545 QObjectPrivate::connect(pTcpSocket, &QAbstractSocket::readyRead, this,
546 &QWebSocketPrivate::processData, Qt::QueuedConnection);
548 const QSslSocket * const sslSocket = qobject_cast<const QSslSocket *>(pTcpSocket);
550 QObject::connect(sslSocket, &QSslSocket::encryptedBytesWritten, q,
551 &QWebSocket::bytesWritten);
552 typedef void (QSslSocket:: *sslErrorSignalType)(const QList<QSslError> &);
553 QObject::connect(sslSocket,
554 static_cast<sslErrorSignalType>(&QSslSocket::sslErrors),
555 q, &QWebSocket::sslErrors);
559 QObject::connect(pTcpSocket, &QAbstractSocket::bytesWritten, q,
560 &QWebSocket::bytesWritten);
564 QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::textFrameReceived, q,
565 &QWebSocket::textFrameReceived);
566 QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryFrameReceived, q,
567 &QWebSocket::binaryFrameReceived);
568 QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryMessageReceived, q,
569 &QWebSocket::binaryMessageReceived);
570 QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::textMessageReceived, q,
571 &QWebSocket::textMessageReceived);
572 QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::errorEncountered, this,
573 &QWebSocketPrivate::close);
574 QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::pingReceived, this,
575 &QWebSocketPrivate::processPing);
576 QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::pongReceived, this,
577 &QWebSocketPrivate::processPong);
578 QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::closeReceived, this,
579 &QWebSocketPrivate::processClose);
585 void QWebSocketPrivate::releaseConnections(const QTcpSocket *pTcpSocket)
587 if (Q_LIKELY(pTcpSocket))
588 pTcpSocket->disconnect(pTcpSocket);
589 m_dataProcessor.disconnect();
595 QWebSocketProtocol::Version QWebSocketPrivate::version() const
603 QString QWebSocketPrivate::resourceName() const
605 return m_resourceName;
611 QUrl QWebSocketPrivate::requestUrl() const
619 QString QWebSocketPrivate::origin() const
627 QString QWebSocketPrivate::protocol() const
635 QString QWebSocketPrivate::extension() const
643 QWebSocketProtocol::CloseCode QWebSocketPrivate::closeCode() const
651 QString QWebSocketPrivate::closeReason() const
653 return m_closeReason;
659 QByteArray QWebSocketPrivate::getFrameHeader(QWebSocketProtocol::OpCode opCode,
660 quint64 payloadLength, quint32 maskingKey,
665 bool ok = payloadLength <= 0x7FFFFFFFFFFFFFFFULL;
668 //FIN, RSV1-3, opcode (RSV-1, RSV-2 and RSV-3 are zero)
669 byte = static_cast<quint8>((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00));
670 header.append(static_cast<char>(byte));
675 if (payloadLength <= 125) {
676 byte |= static_cast<quint8>(payloadLength);
677 header.append(static_cast<char>(byte));
678 } else if (payloadLength <= 0xFFFFU) {
680 header.append(static_cast<char>(byte));
681 quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
682 header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
683 } else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL) {
685 header.append(static_cast<char>(byte));
686 quint64 swapped = qToBigEndian<quint64>(payloadLength);
687 header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8);
690 if (maskingKey != 0) {
691 const quint32 mask = qToBigEndian<quint32>(maskingKey);
692 header.append(static_cast<const char *>(static_cast<const void *>(&mask)),
696 setErrorString(QStringLiteral("WebSocket::getHeader: payload too big!"));
697 Q_EMIT q_ptr->error(QAbstractSocket::DatagramTooLargeError);
706 qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary)
708 qint64 payloadWritten = 0;
709 if (Q_UNLIKELY(!m_pSocket) || (state() != QAbstractSocket::ConnectedState))
710 return payloadWritten;
713 const QWebSocketProtocol::OpCode firstOpCode = isBinary ?
714 QWebSocketProtocol::OpCodeBinary : QWebSocketProtocol::OpCodeText;
716 int numFrames = data.size() / FRAME_SIZE_IN_BYTES;
717 QByteArray tmpData(data);
719 char *payload = tmpData.data();
720 quint64 sizeLeft = quint64(data.size()) % FRAME_SIZE_IN_BYTES;
721 if (Q_LIKELY(sizeLeft))
724 //catch the case where the payload is zero bytes;
725 //in this case, we still need to send a frame
726 if (Q_UNLIKELY(numFrames == 0))
728 quint64 currentPosition = 0;
729 qint64 bytesWritten = 0;
730 quint64 bytesLeft = data.size();
732 for (int i = 0; i < numFrames; ++i) {
733 quint32 maskingKey = 0;
735 maskingKey = generateMaskingKey();
737 const bool isLastFrame = (i == (numFrames - 1));
738 const bool isFirstFrame = (i == 0);
740 const quint64 size = qMin(bytesLeft, FRAME_SIZE_IN_BYTES);
741 const QWebSocketProtocol::OpCode opcode = isFirstFrame ? firstOpCode
742 : QWebSocketProtocol::OpCodeContinue;
745 bytesWritten += m_pSocket->write(getFrameHeader(opcode, size, maskingKey, isLastFrame));
748 if (Q_LIKELY(size > 0)) {
749 char *currentData = payload + currentPosition;
751 QWebSocketProtocol::mask(currentData, size, maskingKey);
752 qint64 written = m_pSocket->write(currentData, static_cast<qint64>(size));
753 if (Q_LIKELY(written > 0)) {
754 bytesWritten += written;
755 payloadWritten += written;
758 setErrorString(QWebSocket::tr("Error writing bytes to socket: %1.")
759 .arg(m_pSocket->errorString()));
760 Q_EMIT q->error(QAbstractSocket::NetworkError);
764 currentPosition += size;
767 if (Q_UNLIKELY(payloadWritten != data.size())) {
768 setErrorString(QWebSocket::tr("Bytes written %1 != %2.")
769 .arg(payloadWritten).arg(data.size()));
770 Q_EMIT q->error(QAbstractSocket::NetworkError);
772 return payloadWritten;
778 quint32 QWebSocketPrivate::generateMaskingKey() const
780 return m_pMaskGenerator->nextMask();
786 QByteArray QWebSocketPrivate::generateKey() const
790 for (int i = 0; i < 4; ++i) {
791 const quint32 tmp = m_pMaskGenerator->nextMask();
792 key.append(static_cast<const char *>(static_cast<const void *>(&tmp)), sizeof(quint32));
795 return key.toBase64();
802 QString QWebSocketPrivate::calculateAcceptKey(const QByteArray &key) const
804 const QByteArray tmpKey = key + QByteArrayLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
805 const QByteArray hash = QCryptographicHash::hash(tmpKey, QCryptographicHash::Sha1).toBase64();
806 return QString::fromLatin1(hash);
812 qint64 QWebSocketPrivate::writeFrames(const QList<QByteArray> &frames)
815 if (Q_LIKELY(m_pSocket)) {
816 QList<QByteArray>::const_iterator it;
817 for (it = frames.cbegin(); it < frames.cend(); ++it)
818 written += writeFrame(*it);
826 qint64 QWebSocketPrivate::writeFrame(const QByteArray &frame)
829 if (Q_LIKELY(m_pSocket))
830 written = m_pSocket->write(frame);
837 QString readLine(QTcpSocket *pSocket)
842 while (pSocket->getChar(&c)) {
843 if (c == char('\r')) {
844 pSocket->getChar(&c);
847 line.append(QChar::fromLatin1(c));
853 // this function is a copy of QHttpNetworkReplyPrivate::parseStatus
854 static bool parseStatusLine(const QByteArray &status, int *majorVersion, int *minorVersion,
855 int *statusCode, QString *reasonPhrase)
858 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
859 // HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
860 // that makes: 'HTTP/n.n xxx Message'
861 // byte count: 0123456789012
863 static const int minLength = 11;
864 static const int dotPos = 6;
865 static const int spacePos = 8;
866 static const char httpMagic[] = "HTTP/";
868 if (status.length() < minLength
869 || !status.startsWith(httpMagic)
870 || status.at(dotPos) != '.'
871 || status.at(spacePos) != ' ') {
872 // I don't know how to parse this status line
876 // optimize for the valid case: defer checking until the end
877 *majorVersion = status.at(dotPos - 1) - '0';
878 *minorVersion = status.at(dotPos + 1) - '0';
881 int j = status.indexOf(' ', i + 1); // j == -1 || at(j) == ' ' so j+1 == 0 && j+1 <= length()
882 const QByteArray code = status.mid(i + 1, j - i - 1);
885 *statusCode = code.toInt(&ok);
886 *reasonPhrase = QString::fromLatin1(status.constData() + j + 1);
888 return ok && uint(*majorVersion) <= 9 && uint(* minorVersion) <= 9;
892 //called on the client for a server handshake response
896 void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket)
899 if (Q_UNLIKELY(!pSocket))
903 QString errorDescription;
905 const QByteArray statusLine = pSocket->readLine();
906 int httpMajorVersion, httpMinorVersion;
908 QString httpStatusMessage;
909 if (Q_UNLIKELY(!parseStatusLine(statusLine, &httpMajorVersion, &httpMinorVersion,
910 &httpStatusCode, &httpStatusMessage))) {
911 errorDescription = QWebSocket::tr("Invalid statusline in response: %1.").arg(QString::fromLatin1(statusLine));
913 QString headerLine = readLine(pSocket);
914 QMap<QString, QString> headers;
915 while (!headerLine.isEmpty()) {
916 const QStringList headerField = headerLine.split(QStringLiteral(": "),
917 QString::SkipEmptyParts);
918 if (headerField.size() == 2) {
919 headers.insertMulti(headerField[0].toLower(), headerField[1]);
921 headerLine = readLine(pSocket);
924 const QString acceptKey = headers.value(QStringLiteral("sec-websocket-accept"),
926 const QString upgrade = headers.value(QStringLiteral("upgrade"), QString());
927 const QString connection = headers.value(QStringLiteral("connection"), QString());
928 // unused for the moment
929 // const QString extensions = headers.value(QStringLiteral("sec-websocket-extensions"),
931 // const QString protocol = headers.value(QStringLiteral("sec-websocket-protocol"),
933 const QString version = headers.value(QStringLiteral("sec-websocket-version"),
936 if (Q_LIKELY(httpStatusCode == 101)) {
937 //HTTP/x.y 101 Switching Protocols
938 //TODO: do not check the httpStatusText right now
939 ok = !(acceptKey.isEmpty() ||
940 (httpMajorVersion < 1 || httpMinorVersion < 1) ||
941 (upgrade.toLower() != QStringLiteral("websocket")) ||
942 (connection.toLower() != QStringLiteral("upgrade")));
944 const QString accept = calculateAcceptKey(m_key);
945 ok = (accept == acceptKey);
948 QWebSocket::tr("Accept-Key received from server %1 does not match the client key %2.")
949 .arg(acceptKey).arg(accept);
952 QWebSocket::tr("QWebSocketPrivate::processHandshake: Invalid statusline in response: %1.")
953 .arg(QString::fromLatin1(statusLine));
955 } else if (httpStatusCode == 400) {
956 //HTTP/1.1 400 Bad Request
957 if (!version.isEmpty()) {
958 const QStringList versions = version.split(QStringLiteral(", "),
959 QString::SkipEmptyParts);
960 if (!versions.contains(QString::number(QWebSocketProtocol::currentVersion()))) {
961 //if needed to switch protocol version, then we are finished here
962 //because we cannot handle other protocols than the RFC one (v13)
964 QWebSocket::tr("Handshake: Server requests a version that we don't support: %1.")
965 .arg(versions.join(QStringLiteral(", ")));
968 //we tried v13, but something different went wrong
970 QWebSocket::tr("QWebSocketPrivate::processHandshake: Unknown error condition encountered. Aborting connection.");
976 QWebSocket::tr("QWebSocketPrivate::processHandshake: Unhandled http status code: %1 (%2).")
977 .arg(httpStatusCode).arg(httpStatusMessage);
982 setErrorString(errorDescription);
983 Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
985 //handshake succeeded
986 setSocketState(QAbstractSocket::ConnectedState);
987 Q_EMIT q->connected();
995 void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketState)
999 QAbstractSocket::SocketState webSocketState = this->state();
1000 switch (socketState) {
1001 case QAbstractSocket::ConnectedState:
1002 if (webSocketState == QAbstractSocket::ConnectingState) {
1003 m_key = generateKey();
1004 const QString handshake =
1005 createHandShakeRequest(m_resourceName,
1007 % QStringLiteral(":")
1008 % QString::number(m_requestUrl.port(80)),
1013 if (handshake.isEmpty()) {
1015 Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
1018 m_pSocket->write(handshake.toLatin1());
1022 case QAbstractSocket::ClosingState:
1023 if (webSocketState == QAbstractSocket::ConnectedState)
1024 setSocketState(QAbstractSocket::ClosingState);
1027 case QAbstractSocket::UnconnectedState:
1028 if (webSocketState != QAbstractSocket::UnconnectedState) {
1029 setSocketState(QAbstractSocket::UnconnectedState);
1030 Q_EMIT q->disconnected();
1034 case QAbstractSocket::HostLookupState:
1035 case QAbstractSocket::ConnectingState:
1036 case QAbstractSocket::BoundState:
1037 case QAbstractSocket::ListeningState:
1039 //to make C++ compiler happy;
1049 void QWebSocketPrivate::processData()
1051 Q_ASSERT(m_pSocket);
1052 while (m_pSocket->bytesAvailable()) {
1053 if (state() == QAbstractSocket::ConnectingState)
1054 processHandshake(m_pSocket.data());
1056 m_dataProcessor.process(m_pSocket.data());
1063 void QWebSocketPrivate::processPing(const QByteArray &data)
1065 Q_ASSERT(m_pSocket);
1066 quint32 maskingKey = 0;
1068 maskingKey = generateMaskingKey();
1069 m_pSocket->write(getFrameHeader(QWebSocketProtocol::OpCodePong, data.size(), maskingKey, true));
1070 if (data.size() > 0) {
1071 QByteArray maskedData = data;
1073 QWebSocketProtocol::mask(&maskedData, maskingKey);
1074 m_pSocket->write(maskedData);
1081 void QWebSocketPrivate::processPong(const QByteArray &data)
1084 Q_EMIT q->pong(static_cast<quint64>(m_pingTimer.elapsed()), data);
1090 void QWebSocketPrivate::processClose(QWebSocketProtocol::CloseCode closeCode, QString closeReason)
1092 m_isClosingHandshakeReceived = true;
1093 close(closeCode, closeReason);
1099 QString QWebSocketPrivate::createHandShakeRequest(QString resourceName,
1106 QStringList handshakeRequest;
1107 if (resourceName.contains(QStringLiteral("\r\n"))) {
1108 setErrorString(QWebSocket::tr("The resource name contains newlines. " \
1109 "Possible attack detected."));
1112 if (host.contains(QStringLiteral("\r\n"))) {
1113 setErrorString(QWebSocket::tr("The hostname contains newlines. " \
1114 "Possible attack detected."));
1117 if (origin.contains(QStringLiteral("\r\n"))) {
1118 setErrorString(QWebSocket::tr("The origin contains newlines. " \
1119 "Possible attack detected."));
1122 if (extensions.contains(QStringLiteral("\r\n"))) {
1123 setErrorString(QWebSocket::tr("The extensions attribute contains newlines. " \
1124 "Possible attack detected."));
1127 if (protocols.contains(QStringLiteral("\r\n"))) {
1128 setErrorString(QWebSocket::tr("The protocols attribute contains newlines. " \
1129 "Possible attack detected."));
1133 handshakeRequest << QStringLiteral("GET ") % resourceName % QStringLiteral(" HTTP/1.1") <<
1134 QStringLiteral("Host: ") % host <<
1135 QStringLiteral("Upgrade: websocket") <<
1136 QStringLiteral("Connection: Upgrade") <<
1137 QStringLiteral("Sec-WebSocket-Key: ") % QString::fromLatin1(key);
1138 if (!origin.isEmpty())
1139 handshakeRequest << QStringLiteral("Origin: ") % origin;
1140 handshakeRequest << QStringLiteral("Sec-WebSocket-Version: ")
1141 % QString::number(QWebSocketProtocol::currentVersion());
1142 if (extensions.length() > 0)
1143 handshakeRequest << QStringLiteral("Sec-WebSocket-Extensions: ") % extensions;
1144 if (protocols.length() > 0)
1145 handshakeRequest << QStringLiteral("Sec-WebSocket-Protocol: ") % protocols;
1146 handshakeRequest << QStringLiteral("\r\n");
1148 return handshakeRequest.join(QStringLiteral("\r\n"));
1154 QAbstractSocket::SocketState QWebSocketPrivate::state() const
1156 return m_socketState;
1162 void QWebSocketPrivate::setSocketState(QAbstractSocket::SocketState state)
1165 if (m_socketState != state) {
1166 m_socketState = state;
1167 Q_EMIT q->stateChanged(m_socketState);
1174 void QWebSocketPrivate::setErrorString(const QString &errorString)
1176 if (m_errorString != errorString)
1177 m_errorString = errorString;
1183 QHostAddress QWebSocketPrivate::localAddress() const
1185 QHostAddress address;
1186 if (Q_LIKELY(m_pSocket))
1187 address = m_pSocket->localAddress();
1194 quint16 QWebSocketPrivate::localPort() const
1197 if (Q_LIKELY(m_pSocket))
1198 port = m_pSocket->localPort();
1205 QAbstractSocket::PauseModes QWebSocketPrivate::pauseMode() const
1213 QHostAddress QWebSocketPrivate::peerAddress() const
1215 QHostAddress address;
1216 if (Q_LIKELY(m_pSocket))
1217 address = m_pSocket->peerAddress();
1224 QString QWebSocketPrivate::peerName() const
1227 if (Q_LIKELY(m_pSocket))
1228 name = m_pSocket->peerName();
1235 quint16 QWebSocketPrivate::peerPort() const
1238 if (Q_LIKELY(m_pSocket))
1239 port = m_pSocket->peerPort();
1243 #ifndef QT_NO_NETWORKPROXY
1247 QNetworkProxy QWebSocketPrivate::proxy() const
1249 return m_configuration.m_proxy;
1255 void QWebSocketPrivate::setProxy(const QNetworkProxy &networkProxy)
1257 if (m_configuration.m_proxy != networkProxy)
1258 m_configuration.m_proxy = networkProxy;
1260 #endif //QT_NO_NETWORKPROXY
1265 void QWebSocketPrivate::setMaskGenerator(const QMaskGenerator *maskGenerator)
1268 m_pMaskGenerator = &m_defaultMaskGenerator;
1269 else if (maskGenerator != m_pMaskGenerator)
1270 m_pMaskGenerator = const_cast<QMaskGenerator *>(maskGenerator);
1276 const QMaskGenerator *QWebSocketPrivate::maskGenerator() const
1278 Q_ASSERT(m_pMaskGenerator);
1279 return m_pMaskGenerator;
1285 qint64 QWebSocketPrivate::readBufferSize() const
1287 return m_readBufferSize;
1293 void QWebSocketPrivate::resume()
1295 if (Q_LIKELY(m_pSocket))
1296 m_pSocket->resume();
1302 void QWebSocketPrivate::setPauseMode(QAbstractSocket::PauseModes pauseMode)
1304 m_pauseMode = pauseMode;
1305 if (Q_LIKELY(m_pSocket))
1306 m_pSocket->setPauseMode(m_pauseMode);
1312 void QWebSocketPrivate::setReadBufferSize(qint64 size)
1314 m_readBufferSize = size;
1315 if (Q_LIKELY(m_pSocket))
1316 m_pSocket->setReadBufferSize(m_readBufferSize);
1322 bool QWebSocketPrivate::isValid() const
1324 return (m_pSocket && m_pSocket->isValid() &&
1325 (m_socketState == QAbstractSocket::ConnectedState));