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)
90 QWebSocketPrivate::QWebSocketPrivate(const QString &origin, QWebSocketProtocol::Version version,
91 QWebSocket *pWebSocket) :
102 m_socketState(QAbstractSocket::UnconnectedState),
103 m_pauseMode(QAbstractSocket::PauseNever),
107 m_isClosingHandshakeSent(false),
108 m_isClosingHandshakeReceived(false),
109 m_closeCode(QWebSocketProtocol::CloseCodeNormal),
114 m_pMaskGenerator(&m_defaultMaskGenerator),
115 m_defaultMaskGenerator()
122 QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version,
123 QWebSocket *pWebSocket) :
126 m_pSocket(pTcpSocket),
127 m_errorString(pTcpSocket->errorString()),
134 m_socketState(pTcpSocket->state()),
135 m_pauseMode(pTcpSocket->pauseMode()),
136 m_readBufferSize(pTcpSocket->readBufferSize()),
139 m_isClosingHandshakeSent(false),
140 m_isClosingHandshakeReceived(false),
141 m_closeCode(QWebSocketProtocol::CloseCodeNormal),
146 m_pMaskGenerator(&m_defaultMaskGenerator),
147 m_defaultMaskGenerator()
154 void QWebSocketPrivate::init()
157 Q_ASSERT(m_pMaskGenerator);
159 m_pMaskGenerator->seed();
162 makeConnections(m_pSocket.data());
169 QWebSocketPrivate::~QWebSocketPrivate()
173 if (state() == QAbstractSocket::ConnectedState)
174 close(QWebSocketProtocol::CloseCodeGoingAway, QWebSocket::tr("Connection closed"));
175 releaseConnections(m_pSocket.data());
181 void QWebSocketPrivate::abort()
190 QAbstractSocket::SocketError QWebSocketPrivate::error() const
192 QAbstractSocket::SocketError err = QAbstractSocket::UnknownSocketError;
193 if (Q_LIKELY(m_pSocket))
194 err = m_pSocket->error();
201 QString QWebSocketPrivate::errorString() const
204 if (!m_errorString.isEmpty())
205 errMsg = m_errorString;
207 errMsg = m_pSocket->errorString();
214 bool QWebSocketPrivate::flush()
217 if (Q_LIKELY(m_pSocket))
218 result = m_pSocket->flush();
225 qint64 QWebSocketPrivate::sendTextMessage(const QString &message)
227 return doWriteFrames(message.toUtf8(), false);
233 qint64 QWebSocketPrivate::sendBinaryMessage(const QByteArray &data)
235 return doWriteFrames(data, true);
242 void QWebSocketPrivate::setSslConfiguration(const QSslConfiguration &sslConfiguration)
244 m_configuration.m_sslConfiguration = sslConfiguration;
250 QSslConfiguration QWebSocketPrivate::sslConfiguration() const
252 return m_configuration.m_sslConfiguration;
258 void QWebSocketPrivate::ignoreSslErrors(const QList<QSslError> &errors)
260 m_configuration.m_ignoredSslErrors = errors;
266 void QWebSocketPrivate::ignoreSslErrors()
268 m_configuration.m_ignoreSslErrors = true;
269 if (Q_LIKELY(m_pSocket)) {
270 QSslSocket *pSslSocket = qobject_cast<QSslSocket *>(m_pSocket.data());
271 if (Q_LIKELY(pSslSocket))
272 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))
308 if (!m_isClosingHandshakeSent) {
310 const quint16 code = qToBigEndian<quint16>(closeCode);
312 payload.append(static_cast<const char *>(static_cast<const void *>(&code)), 2);
313 if (!reason.isEmpty())
314 payload.append(reason.toUtf8());
315 quint32 maskingKey = 0;
317 maskingKey = generateMaskingKey();
318 QWebSocketProtocol::mask(payload.data(), payload.size(), maskingKey);
320 QByteArray frame = getFrameHeader(QWebSocketProtocol::OpCodeClose,
321 payload.size(), maskingKey, true);
322 frame.append(payload);
323 m_pSocket->write(frame);
326 m_isClosingHandshakeSent = true;
328 Q_EMIT q->aboutToClose();
336 void QWebSocketPrivate::open(const QUrl &url, bool mask)
338 //just delete the old socket for the moment;
339 //later, we can add more 'intelligent' handling by looking at the URL
342 if (!url.isValid() || url.toString().contains(QStringLiteral("\r\n"))) {
343 setErrorString(QWebSocket::tr("Invalid URL."));
344 Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
347 QTcpSocket *pTcpSocket = m_pSocket.take();
349 releaseConnections(pTcpSocket);
350 pTcpSocket->deleteLater();
353 if (Q_LIKELY(!m_pSocket)) {
354 m_dataProcessor.clear();
355 m_isClosingHandshakeReceived = false;
356 m_isClosingHandshakeSent = false;
359 QString resourceName = url.path();
360 if (resourceName.contains(QStringLiteral("\r\n"))) {
361 setRequestUrl(QUrl()); //clear requestUrl
362 setErrorString(QWebSocket::tr("Invalid resource name."));
363 Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
366 if (!url.query().isEmpty()) {
367 if (!resourceName.endsWith(QChar::fromLatin1('?'))) {
368 resourceName.append(QChar::fromLatin1('?'));
370 resourceName.append(url.query());
372 if (resourceName.isEmpty())
373 resourceName = QStringLiteral("/");
374 setResourceName(resourceName);
378 if (url.scheme() == QStringLiteral("wss")) {
379 if (!QSslSocket::supportsSsl()) {
380 const QString message =
381 QWebSocket::tr("SSL Sockets are not supported on this platform.");
382 setErrorString(message);
383 Q_EMIT q->error(QAbstractSocket::UnsupportedSocketOperationError);
385 QSslSocket *sslSocket = new QSslSocket;
386 m_pSocket.reset(sslSocket);
387 if (Q_LIKELY(m_pSocket)) {
388 m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
389 m_pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
390 m_pSocket->setReadBufferSize(m_readBufferSize);
391 m_pSocket->setPauseMode(m_pauseMode);
393 makeConnections(m_pSocket.data());
394 QObject::connect(sslSocket, &QSslSocket::encryptedBytesWritten, q,
395 &QWebSocket::bytesWritten);
396 typedef void (QSslSocket:: *sslErrorSignalType)(const QList<QSslError> &);
397 QObject::connect(sslSocket,
398 static_cast<sslErrorSignalType>(&QSslSocket::sslErrors),
399 q, &QWebSocket::sslErrors);
400 setSocketState(QAbstractSocket::ConnectingState);
402 sslSocket->setSslConfiguration(m_configuration.m_sslConfiguration);
403 if (Q_UNLIKELY(m_configuration.m_ignoreSslErrors))
404 sslSocket->ignoreSslErrors();
406 sslSocket->ignoreSslErrors(m_configuration.m_ignoredSslErrors);
407 #ifndef QT_NO_NETWORKPROXY
408 sslSocket->setProxy(m_configuration.m_proxy);
410 sslSocket->connectToHostEncrypted(url.host(), url.port(443));
412 const QString message = QWebSocket::tr("Out of memory.");
413 setErrorString(message);
414 Q_EMIT q->error(QAbstractSocket::SocketResourceError);
419 if (url.scheme() == QStringLiteral("ws")) {
420 m_pSocket.reset(new QTcpSocket);
421 if (Q_LIKELY(m_pSocket)) {
422 m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
423 m_pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
424 m_pSocket->setReadBufferSize(m_readBufferSize);
425 m_pSocket->setPauseMode(m_pauseMode);
427 makeConnections(m_pSocket.data());
428 QObject::connect(m_pSocket.data(), &QAbstractSocket::bytesWritten, q,
429 &QWebSocket::bytesWritten);
430 setSocketState(QAbstractSocket::ConnectingState);
431 #ifndef QT_NO_NETWORKPROXY
432 m_pSocket->setProxy(m_configuration.m_proxy);
434 m_pSocket->connectToHost(url.host(), url.port(80));
436 const QString message = QWebSocket::tr("Out of memory.");
437 setErrorString(message);
438 Q_EMIT q->error(QAbstractSocket::SocketResourceError);
441 const QString message =
442 QWebSocket::tr("Unsupported websockets scheme: %1").arg(url.scheme());
443 setErrorString(message);
444 Q_EMIT q->error(QAbstractSocket::UnsupportedSocketOperationError);
452 void QWebSocketPrivate::ping(const QByteArray &payload)
454 QByteArray payloadTruncated = payload.left(125);
455 m_pingTimer.restart();
456 QByteArray pingFrame = getFrameHeader(QWebSocketProtocol::OpCodePing, payloadTruncated.size(),
457 0 /*do not mask*/, true);
458 pingFrame.append(payloadTruncated);
459 qint64 ret = writeFrame(pingFrame);
465 Sets the version to use for the websocket protocol;
466 this must be set before the socket is opened.
468 void QWebSocketPrivate::setVersion(QWebSocketProtocol::Version version)
470 if (m_version != version)
476 Sets the resource name of the connection; must be set before the socket is openend
478 void QWebSocketPrivate::setResourceName(const QString &resourceName)
480 if (m_resourceName != resourceName)
481 m_resourceName = resourceName;
487 void QWebSocketPrivate::setRequestUrl(const QUrl &requestUrl)
489 if (m_requestUrl != requestUrl)
490 m_requestUrl = requestUrl;
496 void QWebSocketPrivate::setOrigin(const QString &origin)
498 if (m_origin != origin)
505 void QWebSocketPrivate::setProtocol(const QString &protocol)
507 if (m_protocol != protocol)
508 m_protocol = protocol;
514 void QWebSocketPrivate::setExtension(const QString &extension)
516 if (m_extension != extension)
517 m_extension = extension;
523 void QWebSocketPrivate::enableMasking(bool enable)
525 if (m_mustMask != enable)
532 void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket)
534 Q_ASSERT(pTcpSocket);
537 if (Q_LIKELY(pTcpSocket)) {
538 //pass through signals
539 typedef void (QAbstractSocket:: *ASErrorSignal)(QAbstractSocket::SocketError);
540 typedef void (QWebSocket:: *WSErrorSignal)(QAbstractSocket::SocketError);
541 QObject::connect(pTcpSocket,
542 static_cast<ASErrorSignal>(&QAbstractSocket::error),
543 q, static_cast<WSErrorSignal>(&QWebSocket::error));
544 QObject::connect(pTcpSocket, &QAbstractSocket::proxyAuthenticationRequired, q,
545 &QWebSocket::proxyAuthenticationRequired);
546 QObject::connect(pTcpSocket, &QAbstractSocket::readChannelFinished, q,
547 &QWebSocket::readChannelFinished);
548 QObject::connect(pTcpSocket, &QAbstractSocket::aboutToClose, q, &QWebSocket::aboutToClose);
551 QObjectPrivate::connect(pTcpSocket, &QAbstractSocket::stateChanged, this,
552 &QWebSocketPrivate::processStateChanged);
553 //!!!important to use a QueuedConnection here;
554 //with QTcpSocket there is no problem, but with QSslSocket the processing hangs
555 QObjectPrivate::connect(pTcpSocket, &QAbstractSocket::readyRead, this,
556 &QWebSocketPrivate::processData, Qt::QueuedConnection);
559 QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::textFrameReceived, q,
560 &QWebSocket::textFrameReceived);
561 QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryFrameReceived, q,
562 &QWebSocket::binaryFrameReceived);
563 QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryMessageReceived, q,
564 &QWebSocket::binaryMessageReceived);
565 QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::textMessageReceived, q,
566 &QWebSocket::textMessageReceived);
567 QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::errorEncountered, this,
568 &QWebSocketPrivate::close);
569 QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::pingReceived, this,
570 &QWebSocketPrivate::processPing);
571 QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::pongReceived, this,
572 &QWebSocketPrivate::processPong);
573 QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::closeReceived, this,
574 &QWebSocketPrivate::processClose);
580 void QWebSocketPrivate::releaseConnections(const QTcpSocket *pTcpSocket)
582 if (Q_LIKELY(pTcpSocket))
583 pTcpSocket->disconnect(pTcpSocket);
584 m_dataProcessor.disconnect();
590 QWebSocketProtocol::Version QWebSocketPrivate::version() const
598 QString QWebSocketPrivate::resourceName() const
600 return m_resourceName;
606 QUrl QWebSocketPrivate::requestUrl() const
614 QString QWebSocketPrivate::origin() const
622 QString QWebSocketPrivate::protocol() const
630 QString QWebSocketPrivate::extension() const
638 QWebSocketProtocol::CloseCode QWebSocketPrivate::closeCode() const
646 QString QWebSocketPrivate::closeReason() const
648 return m_closeReason;
654 QByteArray QWebSocketPrivate::getFrameHeader(QWebSocketProtocol::OpCode opCode,
655 quint64 payloadLength, quint32 maskingKey,
660 bool ok = payloadLength <= 0x7FFFFFFFFFFFFFFFULL;
663 //FIN, RSV1-3, opcode (RSV-1, RSV-2 and RSV-3 are zero)
664 byte = static_cast<quint8>((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00));
665 header.append(static_cast<char>(byte));
670 if (payloadLength <= 125) {
671 byte |= static_cast<quint8>(payloadLength);
672 header.append(static_cast<char>(byte));
673 } else if (payloadLength <= 0xFFFFU) {
675 header.append(static_cast<char>(byte));
676 quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
677 header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
678 } else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL) {
680 header.append(static_cast<char>(byte));
681 quint64 swapped = qToBigEndian<quint64>(payloadLength);
682 header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8);
685 if (maskingKey != 0) {
686 const quint32 mask = qToBigEndian<quint32>(maskingKey);
687 header.append(static_cast<const char *>(static_cast<const void *>(&mask)),
691 setErrorString(QStringLiteral("WebSocket::getHeader: payload too big!"));
692 Q_EMIT q_ptr->error(QAbstractSocket::DatagramTooLargeError);
701 qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary)
703 qint64 payloadWritten = 0;
704 if (Q_UNLIKELY(!m_pSocket) || (state() != QAbstractSocket::ConnectedState))
705 return payloadWritten;
708 const QWebSocketProtocol::OpCode firstOpCode = isBinary ?
709 QWebSocketProtocol::OpCodeBinary : QWebSocketProtocol::OpCodeText;
711 int numFrames = data.size() / FRAME_SIZE_IN_BYTES;
712 QByteArray tmpData(data);
714 char *payload = tmpData.data();
715 quint64 sizeLeft = quint64(data.size()) % FRAME_SIZE_IN_BYTES;
716 if (Q_LIKELY(sizeLeft))
719 //catch the case where the payload is zero bytes;
720 //in this case, we still need to send a frame
721 if (Q_UNLIKELY(numFrames == 0))
723 quint64 currentPosition = 0;
724 qint64 bytesWritten = 0;
725 quint64 bytesLeft = data.size();
727 for (int i = 0; i < numFrames; ++i) {
728 quint32 maskingKey = 0;
730 maskingKey = generateMaskingKey();
732 const bool isLastFrame = (i == (numFrames - 1));
733 const bool isFirstFrame = (i == 0);
735 const quint64 size = qMin(bytesLeft, FRAME_SIZE_IN_BYTES);
736 const QWebSocketProtocol::OpCode opcode = isFirstFrame ? firstOpCode
737 : QWebSocketProtocol::OpCodeContinue;
740 bytesWritten += m_pSocket->write(getFrameHeader(opcode, size, maskingKey, isLastFrame));
743 if (Q_LIKELY(size > 0)) {
744 char *currentData = payload + currentPosition;
746 QWebSocketProtocol::mask(currentData, size, maskingKey);
747 qint64 written = m_pSocket->write(currentData, static_cast<qint64>(size));
748 if (Q_LIKELY(written > 0)) {
749 bytesWritten += written;
750 payloadWritten += written;
753 setErrorString(QWebSocket::tr("Error writing bytes to socket: %1.")
754 .arg(m_pSocket->errorString()));
755 Q_EMIT q->error(QAbstractSocket::NetworkError);
759 currentPosition += size;
762 if (Q_UNLIKELY(payloadWritten != data.size())) {
763 setErrorString(QWebSocket::tr("Bytes written %1 != %2.")
764 .arg(payloadWritten).arg(data.size()));
765 Q_EMIT q->error(QAbstractSocket::NetworkError);
767 return payloadWritten;
773 quint32 QWebSocketPrivate::generateMaskingKey() const
775 return m_pMaskGenerator->nextMask();
781 QByteArray QWebSocketPrivate::generateKey() const
785 for (int i = 0; i < 4; ++i) {
786 const quint32 tmp = m_pMaskGenerator->nextMask();
787 key.append(static_cast<const char *>(static_cast<const void *>(&tmp)), sizeof(quint32));
790 return key.toBase64();
797 QString QWebSocketPrivate::calculateAcceptKey(const QByteArray &key) const
799 const QByteArray tmpKey = key + QByteArrayLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
800 const QByteArray hash = QCryptographicHash::hash(tmpKey, QCryptographicHash::Sha1).toBase64();
801 return QString::fromLatin1(hash);
807 qint64 QWebSocketPrivate::writeFrames(const QList<QByteArray> &frames)
810 if (Q_LIKELY(m_pSocket)) {
811 QList<QByteArray>::const_iterator it;
812 for (it = frames.cbegin(); it < frames.cend(); ++it)
813 written += writeFrame(*it);
821 qint64 QWebSocketPrivate::writeFrame(const QByteArray &frame)
824 if (Q_LIKELY(m_pSocket))
825 written = m_pSocket->write(frame);
832 QString readLine(QTcpSocket *pSocket)
837 while (pSocket->getChar(&c)) {
838 if (c == char('\r')) {
839 pSocket->getChar(&c);
842 line.append(QChar::fromLatin1(c));
848 //called on the client for a server handshake response
852 void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket)
855 if (Q_UNLIKELY(!pSocket))
859 QString errorDescription;
861 const QString regExpStatusLine(QStringLiteral("^(HTTP/[0-9]+\\.[0-9]+)\\s([0-9]+)\\s(.*)"));
862 const QRegularExpression regExp(regExpStatusLine);
863 const QString statusLine = readLine(pSocket);
864 QString httpProtocol;
866 QString httpStatusMessage;
867 const QRegularExpressionMatch match = regExp.match(statusLine);
868 if (Q_LIKELY(match.hasMatch())) {
869 QStringList tokens = match.capturedTexts();
870 tokens.removeFirst(); //remove the search string
871 if (tokens.length() == 3) {
872 httpProtocol = tokens[0];
873 httpStatusCode = tokens[1].toInt();
874 httpStatusMessage = tokens[2].trimmed();
878 if (Q_UNLIKELY(!ok)) {
879 errorDescription = QWebSocket::tr("Invalid statusline in response: %1.").arg(statusLine);
881 QString headerLine = readLine(pSocket);
882 QMap<QString, QString> headers;
883 while (!headerLine.isEmpty()) {
884 const QStringList headerField = headerLine.split(QStringLiteral(": "),
885 QString::SkipEmptyParts);
886 if (headerField.size() == 2) {
887 headers.insertMulti(headerField[0], headerField[1]);
889 headerLine = readLine(pSocket);
892 const QString acceptKey = headers.value(QStringLiteral("Sec-WebSocket-Accept"),
894 const QString upgrade = headers.value(QStringLiteral("Upgrade"), QString());
895 const QString connection = headers.value(QStringLiteral("Connection"), QString());
896 // unused for the moment
897 // const QString extensions = headers.value(QStringLiteral("Sec-WebSocket-Extensions"),
899 // const QString protocol = headers.value(QStringLiteral("Sec-WebSocket-Protocol"),
901 const QString version = headers.value(QStringLiteral("Sec-WebSocket-Version"),
904 if (Q_LIKELY(httpStatusCode == 101)) {
905 //HTTP/x.y 101 Switching Protocols
906 bool conversionOk = false;
907 const float version = httpProtocol.midRef(5).toFloat(&conversionOk);
908 //TODO: do not check the httpStatusText right now
909 ok = !(acceptKey.isEmpty() ||
910 (!conversionOk || (version < 1.1f)) ||
911 (upgrade.toLower() != QStringLiteral("websocket")) ||
912 (connection.toLower() != QStringLiteral("upgrade")));
914 const QString accept = calculateAcceptKey(m_key);
915 ok = (accept == acceptKey);
918 QWebSocket::tr("Accept-Key received from server %1 does not match the client key %2.")
919 .arg(acceptKey).arg(accept);
922 QWebSocket::tr("QWebSocketPrivate::processHandshake: Invalid statusline in response: %1.")
925 } else if (httpStatusCode == 400) {
926 //HTTP/1.1 400 Bad Request
927 if (!version.isEmpty()) {
928 const QStringList versions = version.split(QStringLiteral(", "),
929 QString::SkipEmptyParts);
930 if (!versions.contains(QString::number(QWebSocketProtocol::currentVersion()))) {
931 //if needed to switch protocol version, then we are finished here
932 //because we cannot handle other protocols than the RFC one (v13)
934 QWebSocket::tr("Handshake: Server requests a version that we don't support: %1.")
935 .arg(versions.join(QStringLiteral(", ")));
938 //we tried v13, but something different went wrong
940 QWebSocket::tr("QWebSocketPrivate::processHandshake: Unknown error condition encountered. Aborting connection.");
946 QWebSocket::tr("QWebSocketPrivate::processHandshake: Unhandled http status code: %1 (%2).")
947 .arg(httpStatusCode).arg(httpStatusMessage);
952 setErrorString(errorDescription);
953 Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
955 //handshake succeeded
956 setSocketState(QAbstractSocket::ConnectedState);
957 Q_EMIT q->connected();
965 void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketState)
969 QAbstractSocket::SocketState webSocketState = this->state();
970 switch (socketState) {
971 case QAbstractSocket::ConnectedState:
972 if (webSocketState == QAbstractSocket::ConnectingState) {
973 m_key = generateKey();
974 const QString handshake =
975 createHandShakeRequest(m_resourceName,
977 % QStringLiteral(":")
978 % QString::number(m_requestUrl.port(80)),
983 if (handshake.isEmpty()) {
985 Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
988 m_pSocket->write(handshake.toLatin1());
992 case QAbstractSocket::ClosingState:
993 if (webSocketState == QAbstractSocket::ConnectedState)
994 setSocketState(QAbstractSocket::ClosingState);
997 case QAbstractSocket::UnconnectedState:
998 if (webSocketState != QAbstractSocket::UnconnectedState) {
999 setSocketState(QAbstractSocket::UnconnectedState);
1000 Q_EMIT q->disconnected();
1004 case QAbstractSocket::HostLookupState:
1005 case QAbstractSocket::ConnectingState:
1006 case QAbstractSocket::BoundState:
1007 case QAbstractSocket::ListeningState:
1009 //to make C++ compiler happy;
1019 void QWebSocketPrivate::processData()
1021 Q_ASSERT(m_pSocket);
1022 while (m_pSocket->bytesAvailable()) {
1023 if (state() == QAbstractSocket::ConnectingState)
1024 processHandshake(m_pSocket.data());
1026 m_dataProcessor.process(m_pSocket.data());
1033 void QWebSocketPrivate::processPing(const QByteArray &data)
1035 Q_ASSERT(m_pSocket);
1036 quint32 maskingKey = 0;
1038 maskingKey = generateMaskingKey();
1039 m_pSocket->write(getFrameHeader(QWebSocketProtocol::OpCodePong, data.size(), maskingKey, true));
1040 if (data.size() > 0) {
1041 QByteArray maskedData = data;
1043 QWebSocketProtocol::mask(&maskedData, maskingKey);
1044 m_pSocket->write(maskedData);
1051 void QWebSocketPrivate::processPong(const QByteArray &data)
1054 Q_EMIT q->pong(static_cast<quint64>(m_pingTimer.elapsed()), data);
1060 void QWebSocketPrivate::processClose(QWebSocketProtocol::CloseCode closeCode, QString closeReason)
1062 m_isClosingHandshakeReceived = true;
1063 close(closeCode, closeReason);
1069 QString QWebSocketPrivate::createHandShakeRequest(QString resourceName,
1076 QStringList handshakeRequest;
1077 if (resourceName.contains(QStringLiteral("\r\n"))) {
1078 setErrorString(QWebSocket::tr("The resource name contains newlines. " \
1079 "Possible attack detected."));
1082 if (host.contains(QStringLiteral("\r\n"))) {
1083 setErrorString(QWebSocket::tr("The hostname contains newlines. " \
1084 "Possible attack detected."));
1087 if (origin.contains(QStringLiteral("\r\n"))) {
1088 setErrorString(QWebSocket::tr("The origin contains newlines. " \
1089 "Possible attack detected."));
1092 if (extensions.contains(QStringLiteral("\r\n"))) {
1093 setErrorString(QWebSocket::tr("The extensions attribute contains newlines. " \
1094 "Possible attack detected."));
1097 if (protocols.contains(QStringLiteral("\r\n"))) {
1098 setErrorString(QWebSocket::tr("The protocols attribute contains newlines. " \
1099 "Possible attack detected."));
1103 handshakeRequest << QStringLiteral("GET ") % resourceName % QStringLiteral(" HTTP/1.1") <<
1104 QStringLiteral("Host: ") % host <<
1105 QStringLiteral("Upgrade: websocket") <<
1106 QStringLiteral("Connection: Upgrade") <<
1107 QStringLiteral("Sec-WebSocket-Key: ") % QString::fromLatin1(key);
1108 if (!origin.isEmpty())
1109 handshakeRequest << QStringLiteral("Origin: ") % origin;
1110 handshakeRequest << QStringLiteral("Sec-WebSocket-Version: ")
1111 % QString::number(QWebSocketProtocol::currentVersion());
1112 if (extensions.length() > 0)
1113 handshakeRequest << QStringLiteral("Sec-WebSocket-Extensions: ") % extensions;
1114 if (protocols.length() > 0)
1115 handshakeRequest << QStringLiteral("Sec-WebSocket-Protocol: ") % protocols;
1116 handshakeRequest << QStringLiteral("\r\n");
1118 return handshakeRequest.join(QStringLiteral("\r\n"));
1124 QAbstractSocket::SocketState QWebSocketPrivate::state() const
1126 return m_socketState;
1132 void QWebSocketPrivate::setSocketState(QAbstractSocket::SocketState state)
1135 if (m_socketState != state) {
1136 m_socketState = state;
1137 Q_EMIT q->stateChanged(m_socketState);
1144 void QWebSocketPrivate::setErrorString(const QString &errorString)
1146 if (m_errorString != errorString)
1147 m_errorString = errorString;
1153 QHostAddress QWebSocketPrivate::localAddress() const
1155 QHostAddress address;
1156 if (Q_LIKELY(m_pSocket))
1157 address = m_pSocket->localAddress();
1164 quint16 QWebSocketPrivate::localPort() const
1167 if (Q_LIKELY(m_pSocket))
1168 port = m_pSocket->localPort();
1175 QAbstractSocket::PauseModes QWebSocketPrivate::pauseMode() const
1183 QHostAddress QWebSocketPrivate::peerAddress() const
1185 QHostAddress address;
1186 if (Q_LIKELY(m_pSocket))
1187 address = m_pSocket->peerAddress();
1194 QString QWebSocketPrivate::peerName() const
1197 if (Q_LIKELY(m_pSocket))
1198 name = m_pSocket->peerName();
1205 quint16 QWebSocketPrivate::peerPort() const
1208 if (Q_LIKELY(m_pSocket))
1209 port = m_pSocket->peerPort();
1213 #ifndef QT_NO_NETWORKPROXY
1217 QNetworkProxy QWebSocketPrivate::proxy() const
1219 return m_configuration.m_proxy;
1225 void QWebSocketPrivate::setProxy(const QNetworkProxy &networkProxy)
1227 if (networkProxy != networkProxy)
1228 m_configuration.m_proxy = networkProxy;
1230 #endif //QT_NO_NETWORKPROXY
1235 void QWebSocketPrivate::setMaskGenerator(const QMaskGenerator *maskGenerator)
1238 m_pMaskGenerator = &m_defaultMaskGenerator;
1239 else if (maskGenerator != m_pMaskGenerator)
1240 m_pMaskGenerator = const_cast<QMaskGenerator *>(maskGenerator);
1246 const QMaskGenerator *QWebSocketPrivate::maskGenerator() const
1248 Q_ASSERT(m_pMaskGenerator);
1249 return m_pMaskGenerator;
1255 qint64 QWebSocketPrivate::readBufferSize() const
1257 return m_readBufferSize;
1263 void QWebSocketPrivate::resume()
1265 if (Q_LIKELY(m_pSocket))
1266 m_pSocket->resume();
1272 void QWebSocketPrivate::setPauseMode(QAbstractSocket::PauseModes pauseMode)
1274 m_pauseMode = pauseMode;
1275 if (Q_LIKELY(m_pSocket))
1276 m_pSocket->setPauseMode(m_pauseMode);
1282 void QWebSocketPrivate::setReadBufferSize(qint64 size)
1284 m_readBufferSize = size;
1285 if (Q_LIKELY(m_pSocket))
1286 m_pSocket->setReadBufferSize(m_readBufferSize);
1292 bool QWebSocketPrivate::isValid() const
1294 return (m_pSocket && m_pSocket->isValid() &&
1295 (m_socketState == QAbstractSocket::ConnectedState));