Skip to content

Commit

Permalink
Added support for mqtt over websockets
Browse files Browse the repository at this point in the history
This feature will only be compiled if explicitly enabled:
qmake CONFIG+=QMQTT_WEBSOCKETS
or:
qbs build qmqtt.webSocketSupport:true

There are 2 reasons:
* websocket support relies on the QWebsocket class which is
  part of the websockets module. Previous versions of qmqtt
  did not rely on this module, so it may not be present on
  systems where old versions of the shared library are in use.
  Adding a new module dependency will make it harder to upgrade
  this systems.
* A call to QWebSocket::open(const QNetworkRequest &) is used,
  which was introduced in Qt 5.6. qmqtt itself requires version
  5.3 or newer.
  • Loading branch information
ejvr committed Sep 30, 2017
1 parent 018523e commit 299df0b
Show file tree
Hide file tree
Showing 15 changed files with 395 additions and 14 deletions.
13 changes: 9 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
language: cpp
compiler:
- gcc

before_install:
- sudo apt-add-repository -y ppa:ubuntu-toolchain-r/test
- sudo apt-add-repository -y ppa:beineri/opt-qt56
- sudo apt-add-repository -y ppa:beineri/opt-qt571-trusty
- sudo apt-get update -qq
- sudo apt-get install -qq qt56base qt56tools g++-4.8
- source /opt/qt56/bin/qt56-env.sh
- export CXX="g++-4.8"
- sudo apt-get install -qq qt57base qt57tools qt57websockets g++-5
- source /opt/qt57/bin/qt57-env.sh
- export CXX="g++-5"
- export CC="gcc-5"

script:
- qmake --version
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ mqtt client for Qt

**Please compile the library with Qt >= 5.3 version. On Windows you need to specify `CONFIG += NO_UNIT_TESTS`, since gtest is not supported.**

To add websocket support, compile the library with Qt >= 5.7, and specify 'CONFIG += QMQTT_WEBSOCKETS'.

Usage
=====

Expand All @@ -26,6 +28,14 @@ Connect using ssl:
client->setPassword("password");
client->connectToHost();

Connect using websockets:

QMQTT::Client *client = new QMQTT::Client("ws://www.example.com/mqtt", "<origin>", QWebSocketProtocol::VersionLatest);
client->setClientId("clientId");
client->setUsername("user");
client->setPassword("password");
client->connectToHost();

Slots
=====

Expand Down
8 changes: 6 additions & 2 deletions src/mqtt/mqtt.pri
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ PRIVATE_HEADERS += \
$$PWD/qmqtt_network_p.h \
$$PWD/qmqtt_socket_p.h \
$$PWD/qmqtt_ssl_socket_p.h \
$$PWD/qmqtt_timer_p.h
$$PWD/qmqtt_timer_p.h \
$$PWD/qmqtt_websocket_p.h \
$$PWD/qmqtt_websocketiodevice_p.h

SOURCES += \
$$PWD/qmqtt_client_p.cpp \
Expand All @@ -30,4 +32,6 @@ SOURCES += \
$$PWD/qmqtt_message_p.cpp \
$$PWD/qmqtt_socket.cpp \
$$PWD/qmqtt_ssl_socket.cpp \
$$PWD/qmqtt_timer.cpp
$$PWD/qmqtt_timer.cpp \
$$PWD/qmqtt_websocket.cpp \
$$PWD/qmqtt_websocketiodevice.cpp
1 change: 1 addition & 0 deletions src/mqtt/mqtt.pro
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
TARGET = qmqtt
QT = core network
QMQTT_WEBSOCKETS: QT += websockets

DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII

Expand Down
28 changes: 20 additions & 8 deletions src/mqtt/mqtt.qbs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Product {
libraryType,
"mqttmodule",
]
property bool webSocketSupport: false
property string libraryType: "dynamiclibrary"
targetName: "qmqtt"

Expand Down Expand Up @@ -87,10 +88,16 @@ Product {

Depends {
name: "Qt"
submodules: [
"core",
"network",
]
property var baseModules: ["core", "network"]

Properties {
condition: webSocketSupport
submodules: baseModules.concat(["websockets"])
}
Properties {
condition: !webSocketSupport
submodules: baseModules
}
}

Export {
Expand All @@ -100,10 +107,15 @@ Product {

Depends {
name: "Qt"
submodules: [
"core",
"network",
]
property var baseModules: ["core", "network"]
Properties {
condition: product.webSocketSupport
submodules: baseModules.concat(["websockets"])
}
Properties {
condition: !product.webSocketSupport
submodules: baseModules
}
}

cpp.includePaths: product.sourceDirectory
Expand Down
14 changes: 14 additions & 0 deletions src/mqtt/qmqtt_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,20 @@ QMQTT::Client::Client(const QString &hostName,
d->init(hostName, port, ssl, ignoreSelfSigned);
}

#ifdef QT_WEBSOCKETS_LIB
QMQTT::Client::Client(const QString& url,
const QString& origin,
QWebSocketProtocol::Version version,
bool ignoreSelfSigned,
QObject* parent)
: QObject(parent)
, d_ptr(new ClientPrivate(this))
{
Q_D(Client);
d->init(url, origin, version, ignoreSelfSigned);
}
#endif // QT_WEBSOCKETS_LIB

QMQTT::Client::Client(NetworkInterface* network,
const QHostAddress& host,
const quint16 port,
Expand Down
13 changes: 13 additions & 0 deletions src/mqtt/qmqtt_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
#include <QScopedPointer>
#include <QHostAddress>

#ifdef QT_WEBSOCKETS_LIB
#include <QWebSocket>
#endif // QT_WEBSOCKETS_LIB

#ifndef QT_NO_SSL
QT_FORWARD_DECLARE_CLASS(QSslConfiguration)
#endif // QT_NO_SSL
Expand Down Expand Up @@ -140,6 +144,15 @@ class Q_MQTT_EXPORT Client : public QObject
const bool ignoreSelfSigned,
QObject* parent = NULL);

#ifdef QT_WEBSOCKETS_LIB
// Create a connection over websockets
Client(const QString& url,
const QString& origin,
QWebSocketProtocol::Version version,
bool ignoreSelfSigned = false,
QObject* parent = NULL);
#endif // QT_WEBSOCKETS_LIB

// for testing purposes only
Client(NetworkInterface* network,
const QHostAddress& host = QHostAddress::LocalHost,
Expand Down
9 changes: 9 additions & 0 deletions src/mqtt/qmqtt_client_p.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,15 @@ void QMQTT::ClientPrivate::init(const QString& hostName, const quint16 port, con
}
}

#ifdef QT_WEBSOCKETS_LIB
void QMQTT::ClientPrivate::init(const QString& url, const QString& origin,
QWebSocketProtocol::Version version, bool ignoreSelfSigned)
{
_hostName = url;
init(new Network(origin, version, ignoreSelfSigned));
}
#endif // QT_WEBSOCKETS_LIB

void QMQTT::ClientPrivate::init(NetworkInterface* network)
{
Q_Q(Client);
Expand Down
11 changes: 11 additions & 0 deletions src/mqtt/qmqtt_client_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
#include "qmqtt_network_p.h"
#include <QTimer>

#ifdef QT_WEBSOCKETS_LIB
#include <QWebSocket>
#endif // QT_WEBSOCKETS_LIB

#ifndef QT_NO_SSL
QT_FORWARD_DECLARE_CLASS(QSslConfiguration)
#endif // QT_NO_SSL
Expand All @@ -55,11 +59,18 @@ class ClientPrivate
const bool ignoreSelfSigned=false);
#endif // QT_NO_SSL
void init(const QString& hostName, const quint16 port, const bool ssl, const bool ignoreSelfSigned);
#ifdef QT_WEBSOCKETS_LIB
void init(const QString& url, const QString &origin, QWebSocketProtocol::Version version,
bool ignoreSelfSigned);
#endif // QT_WEBSOCKETS_LIB
void init(NetworkInterface* network);

QHostAddress _host;
QString _hostName;
quint16 _port;
#ifdef QT_WEBSOCKETS_LIB
QWebSocketProtocol::Version _webSocketVersion;
#endif // QT_WEBSOCKETS_LIB
quint16 _gmid;
MQTTVersion _version;
QString _clientId;
Expand Down
17 changes: 17 additions & 0 deletions src/mqtt/qmqtt_network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "qmqtt_socket_p.h"
#include "qmqtt_ssl_socket_p.h"
#include "qmqtt_timer_p.h"
#include "qmqtt_websocket_p.h"

const QString DEFAULT_HOST_NAME = QStringLiteral("localhost");
const QHostAddress DEFAULT_HOST = QHostAddress::LocalHost;
Expand Down Expand Up @@ -70,6 +71,22 @@ QMQTT::Network::Network(const QSslConfiguration &config, bool ignoreSelfSigned,
}
#endif // QT_NO_SSL

#ifdef QT_WEBSOCKETS_LIB
QMQTT::Network::Network(const QString& origin, QWebSocketProtocol::Version version,
bool ignoreSelfSigned, QObject* parent)
: NetworkInterface(parent)
, _port(DEFAULT_SSL_PORT)
, _hostName(DEFAULT_HOST_NAME)
, _autoReconnect(DEFAULT_AUTORECONNECT)
, _autoReconnectInterval(DEFAULT_AUTORECONNECT_INTERVAL_MS)
, _socket(new QMQTT::WebSocket(origin, version, ignoreSelfSigned))
, _autoReconnectTimer(new QMQTT::Timer)
, _readState(Header)
{
initialize();
}
#endif // QT_WEBSOCKETS_LIB

QMQTT::Network::Network(SocketInterface* socketInterface, TimerInterface* timerInterface,
QObject* parent)
: NetworkInterface(parent)
Expand Down
8 changes: 8 additions & 0 deletions src/mqtt/qmqtt_network_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
#include <QByteArray>
#include <QHostAddress>

#ifdef QT_WEBSOCKETS_LIB
#include <QWebSocket>
#endif // QT_WEBSOCKETS_LIB

#ifndef QT_NO_SSL
QT_FORWARD_DECLARE_CLASS(QSslConfiguration)
#endif // QT_NO_SSL
Expand All @@ -58,6 +62,10 @@ class Network : public NetworkInterface
#ifndef QT_NO_SSL
Network(const QSslConfiguration& config, bool ignoreSelfSigned, QObject* parent = NULL);
#endif // QT_NO_SSL
#ifdef QT_WEBSOCKETS_LIB
Network(const QString& origin, QWebSocketProtocol::Version version, bool ignoreSelfSigned,
QObject* parent = NULL);
#endif // QT_WEBSOCKETS_LIB
Network(SocketInterface* socketInterface, TimerInterface* timerInterface,
QObject* parent = NULL);
~Network();
Expand Down
73 changes: 73 additions & 0 deletions src/mqtt/qmqtt_websocket.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#ifdef QT_WEBSOCKETS_LIB

#include <QNetworkRequest>
#include <QUrl>
#include "qmqtt_websocket_p.h"

QMQTT::WebSocket::WebSocket(const QString& origin, QWebSocketProtocol::Version version,
bool ignoreSelfSigned, QObject* parent)
: SocketInterface(parent)
, _socket(new QWebSocket(origin, version, this))
, _ioDevice(new WebSocketIODevice(_socket, this))
, _ignoreSelfSigned(ignoreSelfSigned)
{
connect(_socket, &QWebSocket::connected, this, &WebSocket::connected);
connect(_socket, &QWebSocket::disconnected, this, &WebSocket::disconnected);
connect(_socket,
static_cast<void (QWebSocket::*)(QAbstractSocket::SocketError)>(&QWebSocket::error),
this,
static_cast<void (SocketInterface::*)(QAbstractSocket::SocketError)>(&SocketInterface::error));
connect(_socket, &QWebSocket::sslErrors, this, &WebSocket::sslErrors);
}

QMQTT::WebSocket::~WebSocket()
{
}

void QMQTT::WebSocket::connectToHost(const QHostAddress& address, quint16 port)
{
Q_UNUSED(address)
Q_UNUSED(port)
qFatal("No supported");
}

void QMQTT::WebSocket::connectToHost(const QString& hostName, quint16 port)
{
Q_UNUSED(port)
QUrl url(hostName);
QNetworkRequest request(url);
request.setRawHeader("Sec-WebSocket-Protocol", "mqtt");
_ioDevice->connectToHost(request);
}

void QMQTT::WebSocket::disconnectFromHost()
{
_socket->disconnect();
}

QAbstractSocket::SocketState QMQTT::WebSocket::state() const
{
return _socket->state();
}

QAbstractSocket::SocketError QMQTT::WebSocket::error() const
{
return _socket->error();
}

void QMQTT::WebSocket::sslErrors(const QList<QSslError> &errors)
{
if (!_ignoreSelfSigned)
return;
foreach (QSslError error, errors)
{
if (error.error() != QSslError::SelfSignedCertificate &&
error.error() != QSslError::SelfSignedCertificateInChain)
{
return;
}
}
_socket->ignoreSslErrors();
}

#endif // QT_WEBSOCKETS_LIB
Loading

0 comments on commit 299df0b

Please sign in to comment.