--- /dev/null
+.tag export-subst
+.gitignore export-ignore
+.gitattributes export-ignore
+.commit-template export-ignore
--- /dev/null
+$Format:%H$
\example echoclient
\title Echo Client Example
\ingroup qtwebsockets-examples
- \brief Explains how to use the websocket API to create a simple echo client.
+ \brief Describes how to use the websocket API for creating a simple echo client.
+
+ The Echo Client Example shows how to use the websocket API to create a simple
+ echo client.
\sa {Echo Server Example}
*/
\example echoserver
\title Echo Server Example
\ingroup qtwebsockets-examples
+ \brief Shows how to create a simple server application that
+ sends back the messages it receives.
- The Echo Server example shows how to create a simple server application that
- sends back the messages that it receives, using the websockets API.
+ The Echo Server Example shows how to create a simple server application that
+ sends back the messages it receives, using the websockets API.
\sa {Echo Client Example}
*/
\example qmlwebsocketclient
\title QML WebSocket Client Example
\ingroup qtwebsockets-examples
+ \brief Explains how to program a QML WebSocket client example.
*/
\example simplechat
\title Simple Chat Example
\ingroup qtwebsockets-examples
+ \brief Shows how to use the QWebSocket and QWebSocketServer classes
+ for creating a minimalistic chat application over websockets.
- The Simple Chat example shows how to use the QWebSocket and QWebSocketServer
+ The Simple Chat Example shows how to use the QWebSocket and QWebSocketServer
classes to create a minimalistic chat application over websockets.
+
*/
\example sslechoclient
\title SSL Echo Client Example
\ingroup qtwebsockets-examples
+ \brief Shows how to use the QWebSocket class to implement an echo
+ client over a secure connection (wss).
The SSL Echo Client example shows how to use the QWebSocket class to implement
an echo client over a secure connection (wss).
\example sslechoserver
\title SSL Echo Server Example
\ingroup qtwebsockets-examples
+ \brief Shows how to use the QWebSocketServer class for implementing a simple
+ echo server over secure sockets (wss).
The SSL Echo Server example shows how to use the QWebSocketServer class
to implement a simple echo server over secure sockets (wss).
\page examples.html
\brief List of Qt WebSocket examples
- The module provides the following examples for reference to
- help understand the API usage:
+ The examples below can be used as a guide to using the API.
\list
\li \l echoclient
****************************************************************************/
#include "sslechoclient.h"
#include <QtCore/QDebug>
+#include <QtWebSockets/QWebSocket>
+#include <QCoreApplication>
QT_USE_NAMESPACE
m_webSocket()
{
connect(&m_webSocket, &QWebSocket::connected, this, &SslEchoClient::onConnected);
+ typedef void (QWebSocket:: *sslErrorsSignal)(const QList<QSslError> &);
+ connect(&m_webSocket, static_cast<sslErrorsSignal>(&QWebSocket::sslErrors),
+ this, &SslEchoClient::onSslErrors);
m_webSocket.open(QUrl(url));
}
//! [constructor]
void SslEchoClient::onTextMessageReceived(QString message)
{
qDebug() << "Message received:" << message;
+ qApp->quit();
+}
+
+void SslEchoClient::onSslErrors(const QList<QSslError> &errors)
+{
+ Q_UNUSED(errors);
+
+ // WARNING: Never ignore SSL errors in production code.
+ // The proper way to handle self-signed certificates is to add a custom root
+ // to the CA store.
+
+ m_webSocket.ignoreSslErrors();
}
//! [onTextMessageReceived]
#include <QtCore/QObject>
#include <QtWebSockets/QWebSocket>
+#include <QtNetwork/QSslError>
+#include <QtCore/QList>
+#include <QtCore/QString>
+#include <QtCore/QUrl>
QT_FORWARD_DECLARE_CLASS(QWebSocket)
public:
explicit SslEchoClient(const QUrl &url, QObject *parent = Q_NULLPTR);
-Q_SIGNALS:
-
-public Q_SLOTS:
-
private Q_SLOTS:
void onConnected();
void onTextMessageReceived(QString message);
+ void onSslErrors(const QList<QSslError> &errors);
private:
QWebSocket m_webSocket;
depends += qtcore qtnetwork qtdoc
-headerdirs += ..
+headerdirs += .. \
../../imports
sourcedirs += .. \
- src
+ src \
../../imports
QT += websockets
\endcode
- \section1 Related information
+ \section1 Reference documentation
\list
\li \l{Qt WebSockets C++ Classes}{C++ Classes}
- \li \l{Qt WebSockets Examples}{Examples}
\li \l{Qt WebSockets QML Types}{QML Types}
\endlist
+
+ \section1 Examples
+
+ The module provides the following examples as a guide to using
+ the API.
+ \l{Qt WebSockets Examples}{Examples}
*/
\title Qt WebSockets C++ Classes
\ingroup modules
\qtvariable websockets
+ \since 5.3
\brief List of C++ classes that provide WebSockets communication.
To use these classes in your application, use the following include
\brief List of QML types that provide WebSockets communication.
\annotatedlist websockets-qml
+
+ The QML types are accessed by using:
+ \code
+ import QtWebSockets 1.0
+ \endcode
*/
** $QT_END_LICENSE$
**
****************************************************************************/
+/*!
+ \class QDefaultMaskGenerator
+
+ \inmodule QtWebSockets
+
+ \brief The QDefaultMaskGenerator class provides the default mask generator for QtWebSockets.
+
+ The WebSockets specification as outlined in {http://tools.ietf.org/html/rfc6455}{RFC 6455}
+ requires that all communication from client to server must be masked. This is to prevent
+ malicious scripts to attack bad behaving proxies.
+ For more information about the importance of good masking,
+ see \l {http://w2spconf.com/2011/papers/websocket.pdf}.
+ The default mask generator uses the cryptographically insecure qrand() function.
+ The best measure against attacks mentioned in the document above,
+ is to use QWebSocket over a secure connection (\e wss://).
+ In general, always be careful to not have 3rd party script access to
+ a QWebSocket in your application.
+
+ \internal
+*/
#include "qdefaultmaskgenerator_p.h"
#include <QDateTime>
QT_BEGIN_NAMESPACE
+/*!
+ Constructs a new QDefaultMaskGenerator with the given \a parent.
+
+ \internal
+*/
QDefaultMaskGenerator::QDefaultMaskGenerator(QObject *parent) :
QMaskGenerator(parent)
{
}
+/*!
+ Destroys the QDefaultMaskGenerator object.
+
+ \internal
+*/
QDefaultMaskGenerator::~QDefaultMaskGenerator()
{
}
+/*!
+ Seeds the QDefaultMaskGenerator using qsrand().
+ When seed() is not called, no seed is used at all.
+
+ \internal
+*/
bool QDefaultMaskGenerator::seed()
{
qsrand(static_cast<uint>(QDateTime::currentMSecsSinceEpoch()));
return true;
}
+/*!
+ Generates a new random mask using the insecure qrand() method.
+
+ \internal
+*/
quint32 QDefaultMaskGenerator::nextMask()
{
return quint32((double(qrand()) / RAND_MAX) * std::numeric_limits<quint32>::max());
/*!
\class QMaskGenerator
- The QMaskGenerator class provides an abstract base for custom 32-bit mask generators.
+
+ \inmodule QtWebSockets
+
+ \brief The QMaskGenerator class provides an abstract base for custom 32-bit mask generators.
The WebSockets specification as outlined in {http://tools.ietf.org/html/rfc6455}{RFC 6455}
requires that all communication from client to server must be masked. This is to prevent
Initializes the QMaskGenerator by seeding the randomizer.
When seed() is not called, it depends on the specific implementation of a subclass if
a default seed is used or no seed is used at all.
+ Returns \e true if seeding succeeds, otherwise false.
*/
/*!
QT_BEGIN_NAMESPACE
/*!
- Creates a new QMaskGenerator object.
+ Creates a new QMaskGenerator object with the given optional QObject \a parent.
*/
QMaskGenerator::QMaskGenerator(QObject *parent) :
QObject(parent)
**
****************************************************************************/
+/*!
+ \class QSslServer
+
+ \inmodule QtWebSockets
+
+ \brief Implements a secure TCP server over SSL.
+
+ \internal
+*/
+
#include "qsslserver_p.h"
#include <QtNetwork/QSslSocket>
QT_BEGIN_NAMESPACE
+/*!
+ Constructs a new QSslServer with the given \a parent.
+
+ \internal
+*/
QSslServer::QSslServer(QObject *parent) :
QTcpServer(parent),
m_sslConfiguration(QSslConfiguration::defaultConfiguration())
{
}
+/*!
+ Destroys the QSslServer.
+
+ All open connections are closed.
+
+ \internal
+*/
QSslServer::~QSslServer()
{
}
+/*!
+ Sets the \a sslConfiguration to use.
+
+ \sa QSslSocket::setSslConfiguration()
+
+ \internal
+*/
void QSslServer::setSslConfiguration(const QSslConfiguration &sslConfiguration)
{
m_sslConfiguration = sslConfiguration;
}
+/*!
+ Returns the current ssl configuration.
+
+ \internal
+*/
QSslConfiguration QSslServer::sslConfiguration() const
{
return m_sslConfiguration;
}
+/*!
+ Called when a new connection is established.
+
+ Converts \a socket to a QSslSocket.
+
+ \internal
+*/
void QSslServer::incomingConnection(qintptr socket)
{
QSslSocket *pSslSocket = new QSslSocket();
Q_DISABLE_COPY(QSslServer)
public:
- QSslServer(QObject *parent = Q_NULLPTR);
+ explicit QSslServer(QObject *parent = Q_NULLPTR);
virtual ~QSslServer();
void setSslConfiguration(const QSslConfiguration &sslConfiguration);
QWebSocket only supports version 13 of the WebSocket protocol, as outlined in
\l {http://tools.ietf.org/html/rfc6455}{RFC 6455}.
+ \note Some proxies do not understand certain HTTP headers used during a web socket handshake.
+ In that case, non-secure web socket connections fail. The best way to mitigate against
+ this problem is to use web sockets over a secure connection.
+
\warning To generate masks, this implementation of WebSockets uses the cryptographically
insecure qrand() function.
For more information about the importance of good masking,
QObjectPrivate(),
q_ptr(pWebSocket),
m_pSocket(),
- m_errorString(QWebSocket::tr("Unknown error")),
+ m_errorString(),
m_version(version),
m_resourceName(),
m_requestUrl(),
if (Q_UNLIKELY(!m_canUpgrade)) {
response << QStringLiteral("HTTP/1.1 400 Bad Request");
QStringList versions;
- Q_FOREACH (QWebSocketProtocol::Version version, supportedVersions)
+ Q_FOREACH (const QWebSocketProtocol::Version &version, supportedVersions)
versions << QString::number(static_cast<int>(version));
response << QStringLiteral("Sec-WebSocket-Version: ")
% versions.join(QStringLiteral(", "));
\l {http://tools.ietf.org/html/rfc6455#page-39} {extensions} and
\l {http://tools.ietf.org/html/rfc6455#page-12} {subprotocols}.
+ \note When working with self-signed certificates, FireFox currently has a
+ \l {https://bugzilla.mozilla.org/show_bug.cgi?id=594502} {bug} that prevents it to
+ connect to a secure websocket server. To work around this problem, first browse to the
+ secure websocket server using https. FireFox will indicate that the certificate is invalid.
+ From here on, the certificate can be added to the exceptions. After this, the secure websockets
+ connection should work.
+
QWebSocketServer only supports version 13 of the WebSocket protocol, as outlined in RFC 6455.
\sa echoserver.html
m_pTcpServer = pSslServer;
if (Q_LIKELY(m_pTcpServer)) {
QObjectPrivate::connect(pSslServer, &QSslServer::newEncryptedConnection,
- this, &QWebSocketServerPrivate::onNewConnection);
+ this, &QWebSocketServerPrivate::onNewConnection,
+ Qt::QueuedConnection);
QObject::connect(pSslServer, &QSslServer::peerVerifyError,
q_ptr, &QWebSocketServer::peerVerifyError);
QObject::connect(pSslServer, &QSslServer::sslErrors,
qWarning() << QWebSocketServer::tr("Sender is not a QTcpSocket. This is a Qt bug!!!");
return;
}
+ //When using Google Chrome the handshake in received in two parts.
+ //Therefore, the readyRead signal is emitted twice.
+ //This is a guard against the BEAST attack.
+ //See: https://www.imperialviolet.org/2012/01/15/beastfollowup.html
+ //For Safari, the handshake is delivered at once
+ //FIXME: For FireFox, the readyRead signal is never emitted
+ //This is a bug in FireFox (see https://bugzilla.mozilla.org/show_bug.cgi?id=594502)
+ if (!pTcpSocket->canReadLine()) {
+ return;
+ }
+ disconnect(pTcpSocket, &QTcpSocket::readyRead,
+ this, &QWebSocketServerPrivate::handshakeReceived);
Q_Q(QWebSocketServer);
bool success = false;
bool isSecure = false;
- disconnect(pTcpSocket, &QTcpSocket::readyRead,
- this, &QWebSocketServerPrivate::handshakeReceived);
-
if (m_pendingConnections.length() >= maxPendingConnections()) {
pTcpSocket->close();
pTcpSocket->deleteLater();
QSignalSpy pongMessageSpy(&dataProcessor, SIGNAL(pongReceived(QByteArray)));
QSignalSpy textMessageSpy(&dataProcessor, SIGNAL(textMessageReceived(QString)));
QSignalSpy binaryMessageSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)));
- QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString, bool)));
+ QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool)));
QSignalSpy binaryFrameSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)));
dataProcessor.process(&buffer);
QSignalSpy pongMessageSpy(&dataProcessor, SIGNAL(pongReceived(QByteArray)));
QSignalSpy textMessageSpy(&dataProcessor, SIGNAL(textMessageReceived(QString)));
QSignalSpy binaryMessageSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)));
- QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString, bool)));
+ QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool)));
QSignalSpy binaryFrameSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)));
data.append(firstByte).append(secondByte);
SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)));
QSignalSpy textMessageSpy(&dataProcessor, SIGNAL(textMessageReceived(QString)));
QSignalSpy binaryMessageSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)));
- QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString, bool)));
+ QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool)));
QSignalSpy binaryFrameSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)));
if (isContinuationFrame)
SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)));
QSignalSpy textMessageSpy(&dataProcessor, SIGNAL(textMessageReceived(QString)));
QSignalSpy binaryMessageSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)));
- QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString, bool)));
+ QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool)));
QSignalSpy binaryFrameSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)));
data.append(firstByte).append(secondByte);
void tst_invalidOrigin();
void tst_sendTextMessage();
void tst_sendBinaryMessage();
+ void tst_errorString();
};
tst_QWebSocket::tst_QWebSocket()
QCOMPARE(socket->origin(), expectedOrigin);
QCOMPARE(socket->version(), expectedVersion);
QCOMPARE(socket->error(), QAbstractSocket::UnknownSocketError);
- //error string defaults to "Unknown error" (localised)
- QVERIFY(!socket->errorString().isEmpty());
+ QVERIFY(socket->errorString().isEmpty());
QVERIFY(!socket->isValid());
QVERIFY(socket->localAddress().isNull());
QCOMPARE(socket->localPort(), quint16(0));
void tst_QWebSocket::tst_sendTextMessage()
{
- //will resolve in another commit
+ //TODO: will resolve in another commit
#ifndef Q_OS_WIN
EchoServer echoServer;
QWebSocket socket;
+
+ //should return 0 because socket is not open yet
+ QCOMPARE(socket.sendTextMessage(QStringLiteral("1234")), 0);
+
QSignalSpy socketConnectedSpy(&socket, SIGNAL(connected()));
QSignalSpy textMessageReceived(&socket, SIGNAL(textMessageReceived(QString)));
QSignalSpy textFrameReceived(&socket, SIGNAL(textFrameReceived(QString,bool)));
void tst_QWebSocket::tst_sendBinaryMessage()
{
- //will resolve in another commit
+ //TODO: will resolve in another commit
#ifndef Q_OS_WIN
EchoServer echoServer;
QWebSocket socket;
+
+ //should return 0 because socket is not open yet
+ QCOMPARE(socket.sendBinaryMessage(QByteArrayLiteral("1234")), 0);
+
QSignalSpy socketConnectedSpy(&socket, SIGNAL(connected()));
QSignalSpy textMessageReceived(&socket, SIGNAL(textMessageReceived(QString)));
QSignalSpy textFrameReceived(&socket, SIGNAL(textFrameReceived(QString,bool)));
socket.close();
- //QTBUG-36762: QWebSocket emits multiplied signals when socket was reopened
+ //QTBUG-36762: QWebSocket emits multiple signals when socket is reopened
socketConnectedSpy.clear();
binaryMessageReceived.clear();
binaryFrameReceived.clear();
#endif
}
+void tst_QWebSocket::tst_errorString()
+{
+ //Check for QTBUG-37228: QWebSocket returns "Unknown Error" for known errors
+ QWebSocket socket;
+
+ //check that the default error string is empty
+ QVERIFY(socket.errorString().isEmpty());
+
+ QSignalSpy errorSpy(&socket, SIGNAL(error(QAbstractSocket::SocketError)));
+
+ socket.open(QUrl(QStringLiteral("ws://someserver.on.mars:9999")));
+
+ if (errorSpy.count() == 0)
+ errorSpy.wait();
+ QCOMPARE(errorSpy.count(), 1);
+ QList<QVariant> arguments = errorSpy.takeFirst();
+ QAbstractSocket::SocketError socketError =
+ qvariant_cast<QAbstractSocket::SocketError>(arguments.at(0));
+ QCOMPARE(socketError, QAbstractSocket::HostNotFoundError);
+ QCOMPARE(socket.errorString(), QStringLiteral("Host not found"));
+}
+
QTEST_MAIN(tst_QWebSocket)
#include "tst_qwebsocket.moc"
QVERIFY(addresses.length() > 0);
QHostAddress peer = m_pWebSocket->peerAddress();
bool found = false;
- Q_FOREACH(QHostAddress a, addresses)
+ Q_FOREACH (const QHostAddress &a, addresses)
{
if (a == peer)
{