emqx / qmqtt

MQTT client for Qt

Home Page:https://www.emqx.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

WebAssembly (WASM) + WebSockets support

inobelar opened this issue · comments

TL;DR: qmqtt does not compiles for WASM with WebSockets support, but this can be easily fixed by excluding SSL-related methods (in qmqtt_websocket.cpp) - because SSL currently not supported for WASM.

Strictly speaking, this not an "issue", just a feedback about use-case. I'm (currently) have no idea, how to make production-ready pool-request - that's why I wrote this descriptive story for much-more skilled devs (to make a decision - what to do about it).

Reason

I'm using qmqtt (with mosquitto broker) in production, in few projects, which simultaneously must work on various platforms (which supports Qt 5.x) - custom embedded linux (with kinda old version of Qt 5.3 and good-old gcc 4.9), desktop-like linux, windows, android. In each of this various cases (various versions of Qt and C++), qmqtt works perfectly out-the-box (even if built-in into projects directly, not linked as lib - for portability), thanks again guys for your great work!

As an WASM-entusiast, for experimental purpose, I compiled Qt for WASM (and huge work projects) even when this support was in the tech-preview stage, and nowadays-latest versions of Qt (5.12 / 5.13) in which it's officially-supported build target. In all cases, qmqtt was compiled and work as expected, given that it was impossible to transfer data from/to the browser except through WebSockets. IMHO, without WebSockets, using qmqtt with WASM have no sense (even if it's compiles). Here I ran into problems.

Problem solving description

Just by following this instructions, on my Kubutu 18.04 x64, I was successfuly built Qt 5.12 with "known good"/recommended emscripten 1.38.16.

Details
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.3 LTS
Release:        18.04
Codename:       bionic

$ qtbase/bin/qmake --version
QMake version 3.1
Using Qt version 5.12.5 in <path/to/installed/qt/libs>

$ em++ --version
emcc (Emscripten gcc/clang-like replacement) 1.38.16 (commit 04d13b99d0630496139fa8f424c62aad65588c7b)

1) qmqtt_ssl_socket.* exclusion

Next, trying to built qmqtt (as Qt-module) with WebSockets support, I was faced with the following compilation errors:

qmqtt_ssl_socket.cpp errors
$ /path/to/qtbase/bin/qmake . "CONFIG += QMQTT_WEBSOCKETS"
$ make

em++ ... /qmqtt/src/mqtt/qmqtt_client_p.cpp
em++ ... /qmqtt/src/mqtt/qmqtt_client.cpp
em++ ... /qmqtt/src/mqtt/qmqtt_frame.cpp
em++ ... /qmqtt/src/mqtt/qmqtt_message.cpp
em++ ... /qmqtt/src/mqtt/qmqtt_network.cpp
em++ ... /qmqtt/src/mqtt/qmqtt_routesubscription.cpp
em++ ... /qmqtt/src/mqtt/qmqtt_router.cpp
em++ ... /qmqtt/src/mqtt/qmqtt_socket.cpp
em++ -c -pipe -O3 -std=c++1z -fvisibility=hidden -fvisibility-inlines-hidden -fno-exceptions -Wall -W -Wdate-time -Winconsistent-missing-override -DQT_NO_CAST_TO_ASCII -DQT_NO_CAST_FROM_ASCII -DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT -DQT_BUILD_QMQTT_LIB -DQT_BUILDING_QT -DQT_ASCII_CAST_WARNINGS -DQT_MOC_COMPAT -DQT_USE_QSTRINGBUILDER -DQT_DEPRECATED_WARNINGS -DQT_DISABLE_DEPRECATED_BEFORE=0x050000 -DQT_NO_EXCEPTIONS -D_LARGEFILE64_SOURCE -D_LARGEFILE_SOURCE -DQT_NO_DEBUG -DQT_WEBSOCKETS_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -I/path/to/qmqtt/src/mqtt -I. -I/path/to/qmqtt/src/mqtt -I../../include -I../../include/QtQmqtt -I../../include/QtQmqtt/1.0.0 -I../../include/QtQmqtt/1.0.0/QtQmqtt -I/path/to/qtbase/qt_installed/include -I/path/to/qtbase/qt_installed/include/QtWebSockets -I/path/to/qtbase/qt_installed/include/QtNetwork -I/path/to/qtbase/qt_installed/include/QtCore -I.moc -I/path/to/qtbase/qt_installed/mkspecs/wasm-emscripten -o .obj/qmqtt_ssl_socket.o /path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:43:19: error: allocation of incomplete type 'QSslSocket'
    , _socket(new QSslSocket(this))
                  ^~~~~~~~~~
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:46:26: note: forward declaration of 'QSslSocket'
QT_FORWARD_DECLARE_CLASS(QSslSocket)
                         ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:46:12: error: member access into incomplete type 'QSslSocket'
    _socket->setSslConfiguration(config);
           ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:46:26: note: forward declaration of 'QSslSocket'
QT_FORWARD_DECLARE_CLASS(QSslSocket)
                         ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:47:30: error: incomplete type 'QSslSocket' named in nested name specifier
    connect(_socket.data(), &QSslSocket::encrypted,    this, &SocketInterface::connected);
                             ^~~~~~~~~~~~
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:46:26: note: forward declaration of 'QSslSocket'
QT_FORWARD_DECLARE_CLASS(QSslSocket)
                         ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:48:30: error: incomplete type 'QSslSocket' named in nested name specifier
    connect(_socket.data(), &QSslSocket::disconnected, this, &SocketInterface::disconnected);
                             ^~~~~~~~~~~~
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:46:26: note: forward declaration of 'QSslSocket'
QT_FORWARD_DECLARE_CLASS(QSslSocket)
                         ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:50:78: error: incomplete type 'QSslSocket' named in nested name specifier
            static_cast<void (QSslSocket::*)(QAbstractSocket::SocketError)>(&QSslSocket::error),
                                                                             ^~~~~~~~~~~~
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:46:26: note: forward declaration of 'QSslSocket'
QT_FORWARD_DECLARE_CLASS(QSslSocket)
                         ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:54:73: error: incomplete type 'QSslSocket' named in nested name specifier
            static_cast<void (QSslSocket::*)(const QList<QSslError>&)>(&QSslSocket::sslErrors),
                                                                        ^~~~~~~~~~~~
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:46:26: note: forward declaration of 'QSslSocket'
QT_FORWARD_DECLARE_CLASS(QSslSocket)
                         ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:41:54: warning: unused parameter 'config' [-Wunused-parameter]
QMQTT::SslSocket::SslSocket(const QSslConfiguration &config, bool ignoreSelfSigned, QObject* parent)
                                                     ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:65:12: error: cannot initialize return object of type 'QIODevice *' with an rvalue of type 'QSslSocket *'
    return _socket.data();
           ^~~~~~~~~~~~~~
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:70:12: error: member access into incomplete type 'QSslSocket'
    _socket->connectToHostEncrypted(address.toString(), port);
           ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:46:26: note: forward declaration of 'QSslSocket'
QT_FORWARD_DECLARE_CLASS(QSslSocket)
                         ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:68:58: warning: unused parameter 'address' [-Wunused-parameter]
void QMQTT::SslSocket::connectToHost(const QHostAddress& address, quint16 port)
                                                         ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:68:75: warning: unused parameter 'port' [-Wunused-parameter]
void QMQTT::SslSocket::connectToHost(const QHostAddress& address, quint16 port)
                                                                          ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:75:12: error: member access into incomplete type 'QSslSocket'
    _socket->connectToHostEncrypted(hostName, port);
           ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:46:26: note: forward declaration of 'QSslSocket'
QT_FORWARD_DECLARE_CLASS(QSslSocket)
                         ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:73:53: warning: unused parameter 'hostName' [-Wunused-parameter]
void QMQTT::SslSocket::connectToHost(const QString& hostName, quint16 port)
                                                    ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:73:71: warning: unused parameter 'port' [-Wunused-parameter]
void QMQTT::SslSocket::connectToHost(const QString& hostName, quint16 port)
                                                                      ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:80:12: error: member access into incomplete type 'QSslSocket'
    _socket->disconnectFromHost();
           ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:46:26: note: forward declaration of 'QSslSocket'
QT_FORWARD_DECLARE_CLASS(QSslSocket)
                         ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:85:19: error: member access into incomplete type 'QSslSocket'
    return _socket->state();
                  ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:46:26: note: forward declaration of 'QSslSocket'
QT_FORWARD_DECLARE_CLASS(QSslSocket)
                         ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:90:19: error: member access into incomplete type 'QSslSocket'
    return _socket->error();
                  ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:46:26: note: forward declaration of 'QSslSocket'
QT_FORWARD_DECLARE_CLASS(QSslSocket)
                         ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:97:24: error: variable has incomplete type 'QSslError'
    foreach (QSslError error, errors)
                       ^
/path/to/qtbase/qt_installed/include/QtCore/qglobal.h:1029:21: note: expanded from macro 'foreach'
#    define foreach Q_FOREACH
                    ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:47:26: note: forward declaration of 'QSslError'
QT_FORWARD_DECLARE_CLASS(QSslError)
                         ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:99:30: error: incomplete type 'QSslError' named in nested name specifier
        if (error.error() != QSslError::SelfSignedCertificate &&
                             ^~~~~~~~~~~
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:47:26: note: forward declaration of 'QSslError'
QT_FORWARD_DECLARE_CLASS(QSslError)
                         ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:100:30: error: incomplete type 'QSslError' named in nested name specifier
            error.error() != QSslError::SelfSignedCertificateInChain)
                             ^~~~~~~~~~~
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:47:26: note: forward declaration of 'QSslError'
QT_FORWARD_DECLARE_CLASS(QSslError)
                         ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:105:12: error: member access into incomplete type 'QSslSocket'
    _socket->ignoreSslErrors();
           ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:46:26: note: forward declaration of 'QSslSocket'
QT_FORWARD_DECLARE_CLASS(QSslSocket)
                         ^
In file included from /path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:36:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:38:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_socketinterface.h:37:
In file included from /path/to/qtbase/qt_installed/include/QtCore/QObject:1:
In file included from /path/to/qtbase/qt_installed/include/QtCore/qobject.h:53:
/path/to/qtbase/qt_installed/include/QtCore/qscopedpointer.h:57:40: error: invalid application of 'sizeof' to an incomplete type 'QSslSocket'
        typedef char IsIncompleteType[ sizeof(T) ? 1 : -1 ];
                                       ^~~~~~~~~
/path/to/qtbase/qt_installed/include/QtCore/qscopedpointer.h:107:18: note: in instantiation of member function 'QScopedPointerDeleter<QSslSocket>::cleanup'
      requested here
        Cleanup::cleanup(oldD);
                 ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:41:19: note: in instantiation of member function 'QScopedPointer<QSslSocket,
      QScopedPointerDeleter<QSslSocket> >::~QScopedPointer' requested here
QMQTT::SslSocket::SslSocket(const QSslConfiguration &config, bool ignoreSelfSigned, QObject* parent)
                  ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:46:26: note: forward declaration of 'QSslSocket'
QT_FORWARD_DECLARE_CLASS(QSslSocket)
                         ^
In file included from /path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:36:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:38:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_socketinterface.h:37:
In file included from /path/to/qtbase/qt_installed/include/QtCore/QObject:1:
In file included from /path/to/qtbase/qt_installed/include/QtCore/qobject.h:53:
/path/to/qtbase/qt_installed/include/QtCore/qscopedpointer.h:60:9: warning: deleting pointer to incomplete type 'QSslSocket' may cause undefined behavior
      [-Wdelete-incomplete]
        delete pointer;
        ^      ~~~~~~~
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:46:26: note: forward declaration of 'QSslSocket'
QT_FORWARD_DECLARE_CLASS(QSslSocket)
                         ^
In file included from /path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:36:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:38:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_socketinterface.h:35:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_global.h:35:
In file included from /path/to/qtbase/qt_installed/include/QtCore/QtGlobal:1:
In file included from /path/to/qtbase/qt_installed/include/QtCore/qglobal.h:1204:
/path/to/qtbase/qt_installed/include/QtCore/qtypeinfo.h:67:20: error: invalid application of 'sizeof' to an incomplete type 'QSslError'
        isLarge = (sizeof(T)>sizeof(void*)),
                   ^~~~~~~~~
/path/to/qtbase/qt_installed/include/QtCore/qlist.h:462:9: note: in instantiation of template class 'QTypeInfo<QSslError>' requested here
    if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) {
        ^
/path/to/qtbase/qt_installed/include/QtCore/qlist.h:816:13: note: in instantiation of member function 'QList<QSslError>::node_copy' requested here
            node_copy(reinterpret_cast<Node *>(p.begin()),
            ^
/path/to/qtbase/qt_installed/include/QtCore/qglobal.h:976:37: note: in instantiation of member function 'QList<QSslError>::QList' requested here
    QForeachContainer(const T &t) : c(t), i(qAsConst(c).begin()), e(qAsConst(c).end()) {}
                                    ^
/path/to/qtbase/qt_installed/include/QtCore/qglobal.h:1004:12: note: in instantiation of member function 'QtPrivate::QForeachContainer<QList<QSslError>
      >::QForeachContainer' requested here
    return QForeachContainer<typename std::decay<T>::type>(std::forward<T>(t));
           ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:97:5: note: in instantiation of function template specialization 'QtPrivate::qMakeForeachContainer<const
      QList<QSslError> &>' requested here
    foreach (QSslError error, errors)
    ^
/path/to/qtbase/qt_installed/include/QtCore/qglobal.h:1029:21: note: expanded from macro 'foreach'
#    define foreach Q_FOREACH
                    ^
/path/to/qtbase/qt_installed/include/QtCore/qglobal.h:1018:36: note: expanded from macro 'Q_FOREACH'
for (auto _container_ = QtPrivate::qMakeForeachContainer(container); \
                                   ^
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:47:26: note: forward declaration of 'QSslError'
QT_FORWARD_DECLARE_CLASS(QSslError)
                         ^
In file included from /path/to/qmqtt/src/mqtt/qmqtt_ssl_socket.cpp:36:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:38:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_socketinterface.h:35:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_global.h:35:
In file included from /path/to/qtbase/qt_installed/include/QtCore/QtGlobal:1:
In file included from /path/to/qtbase/qt_installed/include/QtCore/qglobal.h:1204:
/path/to/qtbase/qt_installed/include/QtCore/qtypeinfo.h:69:18: error: invalid application of 'sizeof' to an incomplete type 'QSslError'
        sizeOf = sizeof(T)
                 ^~~~~~~~~
/path/to/qmqtt/src/mqtt/qmqtt_ssl_socket_p.h:47:26: note: forward declaration of 'QSslError'
QT_FORWARD_DECLARE_CLASS(QSslError)
                         ^
fatal error: too many errors emitted, stopping now [-ferror-limit=]
6 warnings and 20 errors generated.
ERROR:root:compiler frontend failed to generate LLVM bitcode, halting

which was expected, because both QSslSocket and QSslError wrapped by preprocessor check #ifndef QT_NO_SSL, so that types simply not exists in case of Qt for WASM.

Trying to exclude qmqtt_ssl_socket_p.h and qmqtt_ssl_socket.cpp from src/mqtt/qmqtt.pri, I was confuzed, that making next modification not work:

qmqtt.pri patch, that not work
!contains(DEFINES, QT_NO_SSL) {
    PRIVATE_HEADERS += \
        $$PWD/qmqtt_ssl_socket_p.h

    SOURCES += \
        $$PWD/qmqtt_ssl_socket.cpp
}

so I just removed these files from PRIVATE_HEADERS and SOURCES in qmqtt.pri (because still have no idea, how separate them well) and restarted build.

2) Ifndef's

Similar "missing-type" QSslError was produced for qmqtt_websocket.cpp

missing `QSslError` errors in `qmqtt_websocket.cpp`
em++ ... /qmqtt/src/mqtt/qmqtt_client_p.cpp
em++ ... /qmqtt/src/mqtt/qmqtt_client.cpp
em++ ... /qmqtt/src/mqtt/qmqtt_frame.cpp
em++ ... /qmqtt/src/mqtt/qmqtt_message.cpp
em++ ... /qmqtt/src/mqtt/qmqtt_network.cpp
em++ ... /qmqtt/src/mqtt/qmqtt_routesubscription.cpp
em++ ... /qmqtt/src/mqtt/qmqtt_router.cpp
em++ ... /qmqtt/src/mqtt/qmqtt_socket.cpp
em++ ... /qmqtt/src/mqtt/qmqtt_timer.cpp
em++ -c -pipe -O3 -std=c++1z -fvisibility=hidden -fvisibility-inlines-hidden -fno-exceptions -Wall -W -Wdate-time -Winconsistent-missing-override -DQT_NO_CAST_TO_ASCII -DQT_NO_CAST_FROM_ASCII -DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT -DQT_BUILD_QMQTT_LIB -DQT_BUILDING_QT -DQT_ASCII_CAST_WARNINGS -DQT_MOC_COMPAT -DQT_USE_QSTRINGBUILDER -DQT_DEPRECATED_WARNINGS -DQT_DISABLE_DEPRECATED_BEFORE=0x050000 -DQT_NO_EXCEPTIONS -D_LARGEFILE64_SOURCE -D_LARGEFILE_SOURCE -DQT_NO_DEBUG -DQT_WEBSOCKETS_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -I/path/to/qmqtt/src/mqtt -I. -I/path/to/qmqtt/src/mqtt -I../../include -I../../include/QtQmqtt -I../../include/QtQmqtt/1.0.0 -I../../include/QtQmqtt/1.0.0/QtQmqtt -I/path/to/qtbase/qt_installed/include -I/path/to/qtbase/qt_installed/include/QtWebSockets -I/path/to/qtbase/qt_installed/include/QtNetwork -I/path/to/qtbase/qt_installed/include/QtCore -I.moc -I/path/to/qtbase/qt_installed/mkspecs/wasm-emscripten -o .obj/qmqtt_websocket.o /path/to/qmqtt/src/mqtt/qmqtt_websocket.cpp
/path/to/qmqtt/src/mqtt/qmqtt_websocket.cpp:20:18: error: no member named 'setSslConfiguration' in 'QWebSocket'
        _socket->setSslConfiguration(*sslConfig);
        ~~~~~~~  ^
/path/to/qmqtt/src/mqtt/qmqtt_websocket.cpp:27:35: error: no member named 'sslErrors' in 'QWebSocket'
    connect(_socket, &QWebSocket::sslErrors, this, &WebSocket::sslErrors);
                      ~~~~~~~~~~~~^
/path/to/qmqtt/src/mqtt/qmqtt_websocket.cpp:69:24: error: variable has incomplete type 'QSslError'
    foreach (QSslError error, errors)
                       ^
/path/to/qtbase/qt_installed/include/QtCore/qglobal.h:1029:21: note: expanded from macro 'foreach'
#    define foreach Q_FOREACH
                    ^
/path/to/qmqtt/src/mqtt/qmqtt_websocketiodevice_p.h:45:26: note: forward declaration of 'QSslError'
QT_FORWARD_DECLARE_CLASS(QSslError)
                         ^
/path/to/qmqtt/src/mqtt/qmqtt_websocket.cpp:71:30: error: incomplete type 'QSslError' named in nested name specifier
        if (error.error() != QSslError::SelfSignedCertificate &&
                             ^~~~~~~~~~~
/path/to/qmqtt/src/mqtt/qmqtt_websocketiodevice_p.h:45:26: note: forward declaration of 'QSslError'
QT_FORWARD_DECLARE_CLASS(QSslError)
                         ^
/path/to/qmqtt/src/mqtt/qmqtt_websocket.cpp:72:30: error: incomplete type 'QSslError' named in nested name specifier
            error.error() != QSslError::SelfSignedCertificateInChain)
                             ^~~~~~~~~~~
/path/to/qmqtt/src/mqtt/qmqtt_websocketiodevice_p.h:45:26: note: forward declaration of 'QSslError'
QT_FORWARD_DECLARE_CLASS(QSslError)
                         ^
/path/to/qmqtt/src/mqtt/qmqtt_websocket.cpp:77:14: error: no member named 'ignoreSslErrors' in 'QWebSocket'
    _socket->ignoreSslErrors();
    ~~~~~~~  ^
In file included from /path/to/qmqtt/src/mqtt/qmqtt_websocket.cpp:3:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_websocket_p.h:37:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_socketinterface.h:35:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_global.h:35:
In file included from /path/to/qtbase/qt_installed/include/QtCore/QtGlobal:1:
In file included from /path/to/qtbase/qt_installed/include/QtCore/qglobal.h:1204:
/path/to/qtbase/qt_installed/include/QtCore/qtypeinfo.h:67:20: error: invalid application of 'sizeof' to an incomplete type 'QSslError'
        isLarge = (sizeof(T)>sizeof(void*)),
                   ^~~~~~~~~
/path/to/qtbase/qt_installed/include/QtCore/qlist.h:462:9: note: in instantiation of template class 'QTypeInfo<QSslError>' requested here
    if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) {
        ^
/path/to/qtbase/qt_installed/include/QtCore/qlist.h:816:13: note: in instantiation of member function 'QList<QSslError>::node_copy' requested here
            node_copy(reinterpret_cast<Node *>(p.begin()),
            ^
/path/to/qtbase/qt_installed/include/QtCore/qglobal.h:976:37: note: in instantiation of member function 'QList<QSslError>::QList' requested here
    QForeachContainer(const T &t) : c(t), i(qAsConst(c).begin()), e(qAsConst(c).end()) {}
                                    ^
/path/to/qtbase/qt_installed/include/QtCore/qglobal.h:1004:12: note: in instantiation of member function 'QtPrivate::QForeachContainer<QList<QSslError>
      >::QForeachContainer' requested here
    return QForeachContainer<typename std::decay<T>::type>(std::forward<T>(t));
           ^
/path/to/qmqtt/src/mqtt/qmqtt_websocket.cpp:69:5: note: in instantiation of function template specialization 'QtPrivate::qMakeForeachContainer<const
      QList<QSslError> &>' requested here
    foreach (QSslError error, errors)
    ^
/path/to/qtbase/qt_installed/include/QtCore/qglobal.h:1029:21: note: expanded from macro 'foreach'
#    define foreach Q_FOREACH
                    ^
/path/to/qtbase/qt_installed/include/QtCore/qglobal.h:1018:36: note: expanded from macro 'Q_FOREACH'
for (auto _container_ = QtPrivate::qMakeForeachContainer(container); \
                                   ^
/path/to/qmqtt/src/mqtt/qmqtt_websocketiodevice_p.h:45:26: note: forward declaration of 'QSslError'
QT_FORWARD_DECLARE_CLASS(QSslError)
                         ^
In file included from /path/to/qmqtt/src/mqtt/qmqtt_websocket.cpp:3:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_websocket_p.h:37:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_socketinterface.h:35:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_global.h:35:
In file included from /path/to/qtbase/qt_installed/include/QtCore/QtGlobal:1:
In file included from /path/to/qtbase/qt_installed/include/QtCore/qglobal.h:1204:
/path/to/qtbase/qt_installed/include/QtCore/qtypeinfo.h:69:18: error: invalid application of 'sizeof' to an incomplete type 'QSslError'
        sizeOf = sizeof(T)
                 ^~~~~~~~~
/path/to/qmqtt/src/mqtt/qmqtt_websocketiodevice_p.h:45:26: note: forward declaration of 'QSslError'
QT_FORWARD_DECLARE_CLASS(QSslError)
                         ^
In file included from /path/to/qmqtt/src/mqtt/qmqtt_websocket.cpp:3:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_websocket_p.h:37:
In file included from /path/to/qmqtt/src/mqtt/qmqtt_socketinterface.h:37:
In file included from /path/to/qtbase/qt_installed/include/QtCore/QObject:1:
In file included from /path/to/qtbase/qt_installed/include/QtCore/qobject.h:49:
/path/to/qtbase/qt_installed/include/QtCore/qlist.h:462:21: error: incomplete definition of type 'QTypeInfo<QSslError>'
    if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) {
        ~~~~~~~~~~~~^~
/path/to/qtbase/qt_installed/include/QtCore/qlist.h:816:13: note: in instantiation of member function 'QList<QSslError>::node_copy' requested here
            node_copy(reinterpret_cast<Node *>(p.begin()),
            ^
/path/to/qtbase/qt_installed/include/QtCore/qglobal.h:976:37: note: in instantiation of member function 'QList<QSslError>::QList' requested here
    QForeachContainer(const T &t) : c(t), i(qAsConst(c).begin()), e(qAsConst(c).end()) {}
                                    ^
/path/to/qtbase/qt_installed/include/QtCore/qglobal.h:1004:12: note: in instantiation of member function 'QtPrivate::QForeachContainer<QList<QSslError>
      >::QForeachContainer' requested here
    return QForeachContainer<typename std::decay<T>::type>(std::forward<T>(t));
           ^
/path/to/qmqtt/src/mqtt/qmqtt_websocket.cpp:69:5: note: in instantiation of function template specialization 'QtPrivate::qMakeForeachContainer<const
      QList<QSslError> &>' requested here
    foreach (QSslError error, errors)
    ^
/path/to/qtbase/qt_installed/include/QtCore/qglobal.h:1029:21: note: expanded from macro 'foreach'
#    define foreach Q_FOREACH
                    ^
/path/to/qtbase/qt_installed/include/QtCore/qglobal.h:1018:36: note: expanded from macro 'Q_FOREACH'
for (auto _container_ = QtPrivate::qMakeForeachContainer(container); \
                                   ^
9 errors generated.
ERROR:root:compiler frontend failed to generate LLVM bitcode, halting

additionally, QWebSocket::setSslConfiguration and QWebSocket::ignoreSslErrors methods not exists in that case, because them too wrapped by ifndef. Excluding that code during qmqtt build (as Qt-module) by wrapping-up with #ifndef QT_NO_SSL fix this problem (see marked three cases).

patched qmqtt_websocket.cpp
#ifdef QT_WEBSOCKETS_LIB

#include "qmqtt_websocket_p.h"

#include <QNetworkRequest>
#include <QUrl>
#include <QSslError>

QMQTT::WebSocket::WebSocket(const QString& origin,
                            QWebSocketProtocol::Version version,
                            const QSslConfiguration* sslConfig,
                            bool ignoreSelfSigned,
                            QObject* parent)
    : SocketInterface(parent)
    , _socket(new QWebSocket(origin, version, this))
    , _ioDevice(new WebSocketIODevice(_socket, this))
    , _ignoreSelfSigned(ignoreSelfSigned)
{

#ifndef QT_NO_SSL // <--- EXCLUSION 1)
    if (sslConfig != NULL)
        _socket->setSslConfiguration(*sslConfig);
#endif

    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));

#ifndef QT_NO_SSL // <--- EXCLUSION 2)
    connect(_socket, &QWebSocket::sslErrors, this, &WebSocket::sslErrors);
#endif

}

// ...

void QMQTT::WebSocket::sslErrors(const QList<QSslError> &errors)
{
    if (!_ignoreSelfSigned)
        return;

#ifndef QT_NO_SSL // <--- EXCLUSION 3)
    foreach (QSslError error, errors)
    {
        if (error.error() != QSslError::SelfSignedCertificate &&
            error.error() != QSslError::SelfSignedCertificateInChain)
        {
            return;
        }
    }
    _socket->ignoreSslErrors();
#endif
}

#endif // QT_WEBSOCKETS_LIB

That's all. Works like a charm! :)

Well, maybe QMQTT::WebSocket::sslErrors must be completely wrapped/excluded, because with that internal exclusions it looks like a placeholder.


Example of usage (browser client connected to Mosquitto-with-WebSockets)

If you wish, you can add this example somewhere, as minimum-working-sample.

MainWindow.hpp
#ifndef MAIN_WINDOW_HPP
#define MAIN_WINDOW_HPP

#include <QMainWindow>

#include <QLabel>

#include "qmqtt_client.h"

class MainWindow : public QMainWindow
{
    Q_OBJECT

    QLabel* label = nullptr;

    QMQTT::Client *client = nullptr;

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
};

#endif // MAIN_WINDOW_HPP
MainWindow.cpp
#include "MainWindow.hpp"

#include "qmqtt_message.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    label = new QLabel(this);
    this->setCentralWidget(label);

    // -------------------------------------------------------------------------

    client = new QMQTT::Client("ws://localhost:1884/", "/", QWebSocketProtocol::VersionLatest, false, this);
    client->setClientId("BrowserClient");
    // client->setUsername("user");
    // client->setPassword("password");


    connect(client, &QMQTT::Client::connected, this, [this]
    {
        label->setText(label->text() + "\n" + "Connected to host");

        connect(client, &QMQTT::Client::subscribed, this, [this](const QString& topic, const quint8 qos = 0)
        {
            label->setText(label->text() + "\n" + "Subscribed: " + topic);

            client->publish(QMQTT::Message(0, topic, "SomePayload")); // on subscribe - send test message
        });
        client->subscribe("test_topic");
    });

    connect(client, &QMQTT::Client::error, this, [this](const QMQTT::ClientError error)
    {
        label->setText(label->text() + "\n" + "ERROR: " + QString::number((int)error));
    });

    connect(client, &QMQTT::Client::received, this, [this](const QMQTT::Message& message)
    {
        label->setText(label->text() + "\n" + "RECEIVED: " + message.topic() + " :: " + message.payload());
    });

    client->connectToHost();
}

MainWindow::~MainWindow()
{ }
mosquitto launch
$ cat mosquitto_ws.conf 

listener 1883
protocol mqtt

listener 1884
protocol websockets

$ mosquitto -c mosquitto_ws.conf
1574047351: mosquitto version 1.4.15 (build date Tue, 18 Jun 2019 11:42:22 -0300) starting
1574047351: Config loaded from mosquitto_ws.conf.
1574047351: Opening ipv4 listen socket on port 1883.
1574047351: Opening ipv6 listen socket on port 1883.
1574047351: Opening websockets listen socket on port 1884.
browser client launch
$ /path/to/emsdk/emscripten/1.38.16/emrun --browser=firefox ./qmqtt_websockets_proj.html 
Executing /usr/bin/firefox http://localhost:6931/qmqtt_websockets_proj.html
The html page you are running is not emrun-capable. Stdout, stderr and exit(returncode) capture will not work. Recompile the application with the --emrun linker flag to enable this, or pass --no_emrun_detect to emrun to hide this check.

image


PS

Several of my colleagues asked me: Why do I prefer qmqtt over QtMQTT (which works in WASM + WebSockets out of the box)?

  1. It works on almost all versions of Qt, from the oldest (5.3) to the latest (version-independant).
  2. It can be easily integrated into the project. You do not even need to distribute it as a library, and even more so as a Qt-module (it is important in cases where Qt is from the repository, and not compiled from source).
  3. Clearly written and not over-complicated to understand the sources (and patch them).
  4. It is independent of internal implementation (a non-public API that is REALLY incompatible between versions).
  5. It has the same features: ssl, websockets, wasm ;)

IMHO, answers, like these, may be placed, in the README - to clarify the difference between libraries or motivation to use.

@ejvr Please check this!

commented

The issue here is that websocket support was developed assuming that SSL support would always be available. Apparently that's not always true.

I made some changes to the library to make it compile with websocket support but without SSL support (with QT_WEBSOCKETS_LIB set and QT_NO_SSL not set). Judging from the compiler errors in the comment that should do the trick. Right now, I do not have a mqtt+websocket setup, and do not have the stomach to compile QT5 from scratch.

@inobelar So maybe you can let us now this works for you. You can find the changes in the emqtt branch of my personal clone of qmqtt. If everything works out, some cleanup is needed, and I'll create a pull request for this repo.

@ejvr Thanks for the quick response! I will try to test your branch as soon as possible, and I’ve already reviewed your changes.

Unfortunately, it will take me some time - to get acquainted with the use of CMake and Qt+WASM, because I have never encountered such a thing (sad but true :C) I see that you added the ${PROJECT_NAME}_SSL option to CMakeLists.txt, which can hardly be repeated when using regular qmake *.pro-files (which I use every day).

commented

@ejvr I just cloned your branch, compiled and checked the work of my test-browser-app - everything compiles and works! :)

I did not use CMake, but qmake from the compiled Qt, and compiled the project as follows:

$ path/to/qtbase/qt_for_wasm_installed/bin/qmake . "CONFIG+=QMQTT_WEBSOCKETS"
$ make

Also here and here it says that (maybe) CMake still is not supported.

Qmake is the currently supported build system


Unfortunately, the changes in the source code are still not enough, since the qmqtt.pri still contains qmqtt_ssl_socket_p.h and qmqtt_ssl_socket.cpp, which are still build unconditionally (and produces errors on qt-for-wasm). Therefore, to check your changes, I had to exclude them from there again.

qmake conditions to handle that files, like contains(DEFINES, QT_NO_SSL) {...} or even QT_NO_SSL { ... } makes no effect (qmake thinks that macro not defined, even in Qt sources its exists). It seems that this definition is known only during the build of Qt, and is not available from qmake. The search for a solution to this case did not yield results.

My suggestion is the following - src/mqtt/qmqtt.pri should be modified, with the addition of an additional condition (which does not change the default behavior), like QMQTT_WEBSOCKETS { ... } inside it. For example, by adding check for QMQTT_NO_SSL (not QT_NO_SSL):

qmqtt.pri - first case
INCLUDEPATH += $$PWD

PUBLIC_HEADERS += \
    $$PWD/qmqtt_client.h \
    $$PWD/qmqtt_frame.h \
    $$PWD/qmqtt_message.h \
    $$PWD/qmqtt_routesubscription.h \
    $$PWD/qmqtt_routedmessage.h \
    $$PWD/qmqtt_router.h \
    $$PWD/qmqtt_networkinterface.h \
    $$PWD/qmqtt_socketinterface.h \
    $$PWD/qmqtt_timerinterface.h

PRIVATE_HEADERS += \
    $$PWD/qmqtt_client_p.h \
    $$PWD/qmqtt_message_p.h \
    $$PWD/qmqtt_network_p.h \
    $$PWD/qmqtt_socket_p.h \
    $$PWD/qmqtt_timer_p.h

SOURCES += \
    $$PWD/qmqtt_client_p.cpp \
    $$PWD/qmqtt_client.cpp \
    $$PWD/qmqtt_frame.cpp \
    $$PWD/qmqtt_message.cpp \
    $$PWD/qmqtt_network.cpp \
    $$PWD/qmqtt_routesubscription.cpp \
    $$PWD/qmqtt_router.cpp \
    $$PWD/qmqtt_socket.cpp \
    $$PWD/qmqtt_timer.cpp

!contains(CONFIG, QMQTT_NO_SSL) { #like QT_NO_SSL, which not accessible via qmake
    PRIVATE_HEADERS += \
        $$PWD/qmqtt_ssl_socket_p.h
        
    SOURCES += \
        $$PWD/qmqtt_ssl_socket.cpp
}
    
QMQTT_WEBSOCKETS {
    PRIVATE_HEADERS += \
        $$PWD/qmqtt_websocket_p.h \
        $$PWD/qmqtt_websocketiodevice_p.h

    SOURCES += \
        $$PWD/qmqtt_websocket.cpp \
        $$PWD/qmqtt_websocketiodevice.cpp
}

Alternate solution is wrapping that files by using wasm:

qmqtt.pri - second case
!wasm { # Currently Qt for WebAssembly not support SSL
    PRIVATE_HEADERS += \
        $$PWD/qmqtt_ssl_socket_p.h
        
    SOURCES += \
        $$PWD/qmqtt_ssl_socket.cpp
}

Personally, I am inclined to the first case, with an explicit control. It also entails adding instructions to README:


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

To build qmqtt for WebAssembly, also specify CONFIG += QMQTT_NO_SSL to exclude related files from build, due to missing SSL support for this target.

To build qmqtt for WebAssembly with WebSocket support, you need to specify both:
$ qmake . "CONFIG += QMQTT_NO_SSL" "CONFIG += QMQTT_WEBSOCKETS"


commented

I'm not completely sure about the error messages you get when compiling with wasm. As far as I understand, the QT_NO_SSL macro is defined by qmake if qt was compiled without ssl support.

Either QT_NO_SSL is defined when compiling: in that case qmqtt_ssl_socket.cpp will be compiled, but without errors, because all code is skipped due to the #ifndef QT_NO_SSL at the start of the file.

Else QT_NO_SSL is not defined, and everything would compiled with ssl support.

Third option is that the wasm build has no ssl support, and does not set QT_NO_SSL. Excluding the ssl_socket files alone would not fix all compiler errors: there are many other places in the code where we are using ssl dependent code (like QSslConfiguration).

Therefore I propose the solution that seems to be the safest solution:
I took your recommendation, and I explicitly set QT_NO_SSL whenever the QMQTT_NO_SSL config option is set. This means that even on a system with ssl support (like mine) it's possible to compile QMQTT without ssl support.

Can you check my branch again, if the solution works for your setup?

If so, I'll create the pull request (and update the readme).

@ejvr I just built your branch with updates - everything still ok, even better :)

I started building with only one flag: QMQTT_WEBSOCKETS (without new QMQTT_NO_SSL). There were compilation errors in qmqtt_ssl_socket.cpp again (the same as the text sheet in issue), even if all code wrapped by #ifndef QT_NO_SSL. I agree, this looks very strange. BUT, when i swapped ordering:

from

34 | #ifndef QT_NO_SSL
35 |
36 | #include "qmqtt_ssl_socket_p.h"

to

34 | #include "qmqtt_ssl_socket_p.h"
35 |
36 | #ifndef QT_NO_SSL

everything compiles fine! 😄


Additional note about compiling with both flags (QMQTT_WEBSOCKETS & QMQTT_NO_SSL) - it compiles, BUT next new lines in qmqtt.pri

42 | contains(CONFIG, QMQTT_NO_SSL) {
43 |   DEFINES += QT_NO_SSL
44 | }

produces warning messages for multiple files - almost all, which contains this macro check:

qtbase/qt_installed/include/QtNetwork/qtnetwork-config.h:21:9: warning: 'QT_NO_SSL' macro redefined
      [-Wmacro-redefined]
#define QT_NO_SSL 
        ^
<command line>:7:9: note: previous definition is here
#define QT_NO_SSL 1
        ^
1 warning generated.

IMHO, QMQTT_NO_SSL now unnecessary (after swapping order in qmqtt_ssl_socket.cpp). Adding mention of WASM support to README would still be cool)) Thanks for your work :)

commented

Thanks @inobelar . So let's wrap this up. I have removed the QT_NO_SSL part from the qmake config, and swapped the #include with the #define. Why the latter works is a bit of a mystery to me, but it cannot do any harm either.

Finally, the readme was adjusted as well. But only with a single line indicating that wasm is supported now. If I'm not mistaken the build for wasm only needs the QMQTT_WEBSOCKET config.

The inclusion of the text of why to use this library instead of the one supplied by QT is left to our marketing department (@mwallnoefer?).

@inobelar Can you do verify all of this?

One day I'd like to try using QMQTT with WebAssembly. But not today...

@ejvr Thanks as always for your high-quality pull requests which I can merge seamlessly.

Regarding marketing, I wanna say that I am not the marketing guy but rather a volunteer which helps to coordinate the bug reporters and the experts.

The democratic decision taken together with eqmx was that qmqtt should be moved into maintenance (reference: #167) and pretty all new development should be done in Qt's mqtt. So if we are talking about integrating little changes/error corrections it is fine to do so, otherwise I would be more hesitant in accepting them.

commented

@inobelar Can you do verify all of this?

I cloned the current master branch (with merged changes), and confirm that everything works well :)