/* **************************************************************************** * Copyright 2019 Open Systems Development BV * * * * Permission is hereby granted, free of charge, to any person obtaining a * * copy of this software and associated documentation files (the "Software"), * * to deal in the Software without restriction, including without limitation * * the rights to use, copy, modify, merge, publish, distribute, sublicense, * * and/or sell copies of the Software, and to permit persons to whom the * * Software is furnished to do so, subject to the following conditions: * * * * The above copyright notice and this permission notice shall be included in * * all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * * DEALINGS IN THE SOFTWARE. * * ***************************************************************************/ #include "qmqtt_network_p.h" #include "qmqtt_socket_p.h" #include "qmqtt_ssl_socket_p.h" #include "qmqtt_timer_p.h" #include "qmqtt_websocket_p.h" #include "qmqtt_frame.h" #include const quint16 DEFAULT_PORT = 1883; const quint16 DEFAULT_SSL_PORT = 8883; const bool DEFAULT_AUTORECONNECT = false; const int DEFAULT_AUTORECONNECT_INTERVAL_MS = 5000; QMQTT::Network::Network(QObject* parent) : NetworkInterface(parent) , _port(DEFAULT_PORT) , _autoReconnect(DEFAULT_AUTORECONNECT) , _autoReconnectInterval(DEFAULT_AUTORECONNECT_INTERVAL_MS) , _socket(new QMQTT::Socket) , _autoReconnectTimer(new QMQTT::Timer) , _readState(Header) { initialize(); } #ifndef QT_NO_SSL QMQTT::Network::Network(const QSslConfiguration& config, QObject *parent) : NetworkInterface(parent) , _port(DEFAULT_SSL_PORT) , _autoReconnect(DEFAULT_AUTORECONNECT) , _autoReconnectInterval(DEFAULT_AUTORECONNECT_INTERVAL_MS) , _socket(new QMQTT::SslSocket(config)) , _autoReconnectTimer(new QMQTT::Timer) , _readState(Header) { initialize(); connect(_socket, &QMQTT::SslSocket::sslErrors, this, &QMQTT::Network::sslErrors); } #endif // QT_NO_SSL #ifdef QT_WEBSOCKETS_LIB #ifndef QT_NO_SSL QMQTT::Network::Network(const QString& origin, QWebSocketProtocol::Version version, const QSslConfiguration* sslConfig, QObject* parent) : NetworkInterface(parent) , _port(DEFAULT_SSL_PORT) , _autoReconnect(DEFAULT_AUTORECONNECT) , _autoReconnectInterval(DEFAULT_AUTORECONNECT_INTERVAL_MS) , _socket(new QMQTT::WebSocket(origin, version, sslConfig)) , _autoReconnectTimer(new QMQTT::Timer) , _readState(Header) { initialize(); } #endif // QT_NO_SSL QMQTT::Network::Network(const QString& origin, QWebSocketProtocol::Version version, QObject* parent) : NetworkInterface(parent) , _port(DEFAULT_PORT) , _autoReconnect(DEFAULT_AUTORECONNECT) , _autoReconnectInterval(DEFAULT_AUTORECONNECT_INTERVAL_MS) , _socket(new QMQTT::WebSocket(origin, version)) , _autoReconnectTimer(new QMQTT::Timer) , _readState(Header) { initialize(); } #endif // QT_WEBSOCKETS_LIB QMQTT::Network::Network(SocketInterface* socketInterface, TimerInterface* timerInterface, QObject* parent) : NetworkInterface(parent) , _port(DEFAULT_PORT) , _autoReconnect(DEFAULT_AUTORECONNECT) , _autoReconnectInterval(DEFAULT_AUTORECONNECT_INTERVAL_MS) , _socket(socketInterface) , _autoReconnectTimer(timerInterface) , _readState(Header) { initialize(); } void QMQTT::Network::initialize() { _socket->setParent(this); _autoReconnectTimer->setParent(this); _autoReconnectTimer->setSingleShot(true); _autoReconnectTimer->setInterval(_autoReconnectInterval); QObject::connect(_socket, &SocketInterface::connected, this, &Network::connected); QObject::connect(_socket, &SocketInterface::disconnected, this, &Network::onDisconnected); QObject::connect(_socket->ioDevice(), &QIODevice::readyRead, this, &Network::onSocketReadReady); QObject::connect( _autoReconnectTimer, &TimerInterface::timeout, this, static_cast(&Network::connectToHost)); QObject::connect(_socket, static_cast(&SocketInterface::error), this, &Network::onSocketError); } QMQTT::Network::~Network() { } bool QMQTT::Network::isConnectedToHost() const { return _socket->state() == QAbstractSocket::ConnectedState; } void QMQTT::Network::connectToHost(const QHostAddress& host, const quint16 port) { // Reset the hostname, because if it is not empty connectToHost() will use it instead of _host. _hostName.clear(); _host = host; _port = port; connectToHost(); } void QMQTT::Network::connectToHost(const QString& hostName, const quint16 port) { _hostName = hostName; _port = port; connectToHost(); } void QMQTT::Network::connectToHost() { _readState = Header; if (_hostName.isEmpty()) { _socket->connectToHost(_host, _port); } else { _socket->connectToHost(_hostName, _port); } } void QMQTT::Network::onSocketError(QAbstractSocket::SocketError socketError) { emit error(socketError); if(_autoReconnect) { _autoReconnectTimer->start(); } } void QMQTT::Network::sendFrame(const Frame& frame) { if(_socket->state() == QAbstractSocket::ConnectedState) { QDataStream out(_socket->ioDevice()); frame.write(out); } } void QMQTT::Network::disconnectFromHost() { _socket->disconnectFromHost(); } QAbstractSocket::SocketState QMQTT::Network::state() const { return _socket->state(); } bool QMQTT::Network::autoReconnect() const { return _autoReconnect; } void QMQTT::Network::setAutoReconnect(const bool autoReconnect) { _autoReconnect = autoReconnect; } int QMQTT::Network::autoReconnectInterval() const { return _autoReconnectInterval; } void QMQTT::Network::setAutoReconnectInterval(const int autoReconnectInterval) { _autoReconnectInterval = autoReconnectInterval; _autoReconnectTimer->setInterval(_autoReconnectInterval); } void QMQTT::Network::onSocketReadReady() { QIODevice *ioDevice = _socket->ioDevice(); // Only read the available (cached) bytes, so the read will never block. QByteArray data = ioDevice->read(ioDevice->bytesAvailable()); foreach(char byte, data) { switch (_readState) { case Header: _header = static_cast(byte); _readState = Length; _length = 0; _shift = 0; _data.resize(0); // keep allocated buffer break; case Length: _length |= (byte & 0x7F) << _shift; _shift += 7; if ((byte & 0x80) != 0) break; if (_length == 0) { _readState = Header; Frame frame(_header, _data); emit received(frame); break; } _readState = PayLoad; _data.reserve(_length); break; case PayLoad: _data.append(byte); --_length; if (_length > 0) break; _readState = Header; Frame frame(_header, _data); emit received(frame); break; } } } void QMQTT::Network::onDisconnected() { emit disconnected(); if(_autoReconnect) { _autoReconnectTimer->start(); } } #ifndef QT_NO_SSL void QMQTT::Network::ignoreSslErrors(const QList& errors) { _socket->ignoreSslErrors(errors); } void QMQTT::Network::ignoreSslErrors() { _socket->ignoreSslErrors(); } QSslConfiguration QMQTT::Network::sslConfiguration() const { return _socket->sslConfiguration(); } void QMQTT::Network::setSslConfiguration(const QSslConfiguration& config) { _socket->setSslConfiguration(config); } #endif // QT_NO_SSL