Simplify the code that reads from the socket line by line
Since sockets are buffered `QIODevices` we can use `canReadLine()` to check if we have a full line, instead of using a custom `SocketLineReader` class (and the copy-pasted `DeviceLineReader` in the Bluetooth backend). We can also loop through all the lines instead of queuing calls to `dataReceived`. And we don't need transactions.
This commit is contained in:
parent
0a3ca9e90b
commit
5796b561bf
13 changed files with 47 additions and 410 deletions
|
@ -32,7 +32,6 @@ set(kdeconnectcore_SRCS
|
||||||
backends/linkprovider.cpp
|
backends/linkprovider.cpp
|
||||||
backends/devicelink.cpp
|
backends/devicelink.cpp
|
||||||
backends/pairinghandler.cpp
|
backends/pairinghandler.cpp
|
||||||
backends/devicelinereader.cpp
|
|
||||||
|
|
||||||
kdeconnectplugin.cpp
|
kdeconnectplugin.cpp
|
||||||
kdeconnectpluginconfig.cpp
|
kdeconnectpluginconfig.cpp
|
||||||
|
|
|
@ -19,12 +19,11 @@ BluetoothDeviceLink::BluetoothDeviceLink(const DeviceInfo &deviceInfo,
|
||||||
ConnectionMultiplexer *connection,
|
ConnectionMultiplexer *connection,
|
||||||
QSharedPointer<MultiplexChannel> socket)
|
QSharedPointer<MultiplexChannel> socket)
|
||||||
: DeviceLink(deviceInfo.id, parent)
|
: DeviceLink(deviceInfo.id, parent)
|
||||||
, mSocketReader(new DeviceLineReader(socket.data(), this))
|
|
||||||
, mConnection(connection)
|
, mConnection(connection)
|
||||||
, mChannel(socket)
|
, mChannel(socket)
|
||||||
, mDeviceInfo(deviceInfo)
|
, mDeviceInfo(deviceInfo)
|
||||||
{
|
{
|
||||||
connect(mSocketReader, &DeviceLineReader::readyRead, this, &BluetoothDeviceLink::dataReceived);
|
connect(socket.data(), &QIODevice::readyRead, this, &BluetoothDeviceLink::dataReceived);
|
||||||
|
|
||||||
// We take ownership of the connection.
|
// We take ownership of the connection.
|
||||||
// When the link provider destroys us,
|
// When the link provider destroys us,
|
||||||
|
@ -42,31 +41,26 @@ bool BluetoothDeviceLink::sendPacket(NetworkPacket &np)
|
||||||
uploadJob->start();
|
uploadJob->start();
|
||||||
}
|
}
|
||||||
// TODO: handle too-big packets
|
// TODO: handle too-big packets
|
||||||
int written = mSocketReader->write(np.serialize());
|
int written = mChannel->write(np.serialize());
|
||||||
return (written != -1);
|
return (written != -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothDeviceLink::dataReceived()
|
void BluetoothDeviceLink::dataReceived()
|
||||||
{
|
{
|
||||||
if (mSocketReader->bytesAvailable() == 0)
|
while (mChannel->canReadLine()) {
|
||||||
return;
|
const QByteArray serializedPacket = mChannel->readLine();
|
||||||
|
|
||||||
const QByteArray serializedPacket = mSocketReader->readLine();
|
// qCDebug(KDECONNECT_CORE) << "BluetoothDeviceLink dataReceived" << packet;
|
||||||
|
|
||||||
// qCDebug(KDECONNECT_CORE) << "BluetoothDeviceLink dataReceived" << packet;
|
NetworkPacket packet;
|
||||||
|
NetworkPacket::unserialize(serializedPacket, &packet);
|
||||||
|
|
||||||
NetworkPacket packet;
|
if (packet.hasPayloadTransferInfo()) {
|
||||||
NetworkPacket::unserialize(serializedPacket, &packet);
|
BluetoothDownloadJob *downloadJob = new BluetoothDownloadJob(mConnection, packet.payloadTransferInfo(), this);
|
||||||
|
downloadJob->start();
|
||||||
|
packet.setPayload(downloadJob->payload(), packet.payloadSize());
|
||||||
|
}
|
||||||
|
|
||||||
if (packet.hasPayloadTransferInfo()) {
|
Q_EMIT receivedPacket(packet);
|
||||||
BluetoothDownloadJob *downloadJob = new BluetoothDownloadJob(mConnection, packet.payloadTransferInfo(), this);
|
|
||||||
downloadJob->start();
|
|
||||||
packet.setPayload(downloadJob->payload(), packet.payloadSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_EMIT receivedPacket(packet);
|
|
||||||
|
|
||||||
if (mSocketReader->bytesAvailable() > 0) {
|
|
||||||
QMetaObject::invokeMethod(this, &BluetoothDeviceLink::dataReceived, Qt::QueuedConnection);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#include <QSslCertificate>
|
#include <QSslCertificate>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "../devicelinereader.h"
|
|
||||||
#include "../devicelink.h"
|
#include "../devicelink.h"
|
||||||
|
|
||||||
class ConnectionMultiplexer;
|
class ConnectionMultiplexer;
|
||||||
|
@ -40,7 +39,6 @@ private Q_SLOTS:
|
||||||
void dataReceived();
|
void dataReceived();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DeviceLineReader *mSocketReader;
|
|
||||||
ConnectionMultiplexer *mConnection;
|
ConnectionMultiplexer *mConnection;
|
||||||
QSharedPointer<MultiplexChannel> mChannel;
|
QSharedPointer<MultiplexChannel> mChannel;
|
||||||
DeviceInfo mDeviceInfo;
|
DeviceInfo mDeviceInfo;
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
/**
|
|
||||||
* SPDX-FileCopyrightText: 2013 Albert Vaca <albertvaka@gmail.com>
|
|
||||||
* SPDX-FileCopyrightText: 2014 Alejandro Fiestas Olivares <afiestas@kde.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "devicelinereader.h"
|
|
||||||
|
|
||||||
DeviceLineReader::DeviceLineReader(QIODevice *device, QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
, m_device(device)
|
|
||||||
{
|
|
||||||
connect(m_device, &QIODevice::readyRead, this, &DeviceLineReader::dataReceived);
|
|
||||||
connect(m_device, &QIODevice::aboutToClose, this, &DeviceLineReader::disconnected);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeviceLineReader::dataReceived()
|
|
||||||
{
|
|
||||||
while (m_device->canReadLine()) {
|
|
||||||
const QByteArray line = m_device->readLine();
|
|
||||||
if (line.length() > 1) {
|
|
||||||
m_packets.enqueue(line); // we don't want single \n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we still have things to read from the device, call dataReceived again
|
|
||||||
// We do this manually because we do not trust readyRead to be emitted again
|
|
||||||
// So we call this method again just in case.
|
|
||||||
if (m_device->bytesAvailable() > 0) {
|
|
||||||
QMetaObject::invokeMethod(this, "dataReceived", Qt::QueuedConnection);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have any packets, tell it to the world.
|
|
||||||
if (!m_packets.isEmpty()) {
|
|
||||||
Q_EMIT readyRead();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
/**
|
|
||||||
* SPDX-FileCopyrightText: 2013 Albert Vaca <albertvaka@gmail.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef DEVICELINEREADER_H
|
|
||||||
#define DEVICELINEREADER_H
|
|
||||||
|
|
||||||
#include <QIODevice>
|
|
||||||
#include <QObject>
|
|
||||||
#include <QQueue>
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Encapsulates a QIODevice and implements the same methods of its API that are
|
|
||||||
* used by LanDeviceLink and BluetoothDeviceLink, but readyRead is emitted only
|
|
||||||
* when a newline is found.
|
|
||||||
*/
|
|
||||||
class DeviceLineReader : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
DeviceLineReader(QIODevice *device, QObject *parent = 0);
|
|
||||||
|
|
||||||
QByteArray readLine()
|
|
||||||
{
|
|
||||||
return m_packets.dequeue();
|
|
||||||
}
|
|
||||||
qint64 write(const QByteArray &data)
|
|
||||||
{
|
|
||||||
return m_device->write(data);
|
|
||||||
}
|
|
||||||
qint64 bytesAvailable() const
|
|
||||||
{
|
|
||||||
return m_packets.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void readyRead();
|
|
||||||
void disconnected();
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void dataReceived();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QByteArray m_lastChunk;
|
|
||||||
QIODevice *m_device;
|
|
||||||
QQueue<QByteArray> m_packets;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -7,7 +7,5 @@ set(backends_kdeconnect_SRCS
|
||||||
backends/lan/landevicelink.cpp
|
backends/lan/landevicelink.cpp
|
||||||
backends/lan/compositeuploadjob.cpp
|
backends/lan/compositeuploadjob.cpp
|
||||||
backends/lan/uploadjob.cpp
|
backends/lan/uploadjob.cpp
|
||||||
backends/lan/socketlinereader.cpp
|
|
||||||
|
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|
|
@ -13,11 +13,10 @@
|
||||||
#include "kdeconnectconfig.h"
|
#include "kdeconnectconfig.h"
|
||||||
#include "lanlinkprovider.h"
|
#include "lanlinkprovider.h"
|
||||||
#include "plugins/share/shareplugin.h"
|
#include "plugins/share/shareplugin.h"
|
||||||
#include "socketlinereader.h"
|
|
||||||
|
|
||||||
LanDeviceLink::LanDeviceLink(const DeviceInfo &deviceInfo, LanLinkProvider *parent, QSslSocket *socket)
|
LanDeviceLink::LanDeviceLink(const DeviceInfo &deviceInfo, LanLinkProvider *parent, QSslSocket *socket)
|
||||||
: DeviceLink(deviceInfo.id, parent)
|
: DeviceLink(deviceInfo.id, parent)
|
||||||
, m_socketLineReader(nullptr)
|
, m_socket(nullptr)
|
||||||
, m_deviceInfo(deviceInfo)
|
, m_deviceInfo(deviceInfo)
|
||||||
{
|
{
|
||||||
reset(socket);
|
reset(socket);
|
||||||
|
@ -25,29 +24,24 @@ LanDeviceLink::LanDeviceLink(const DeviceInfo &deviceInfo, LanLinkProvider *pare
|
||||||
|
|
||||||
void LanDeviceLink::reset(QSslSocket *socket)
|
void LanDeviceLink::reset(QSslSocket *socket)
|
||||||
{
|
{
|
||||||
if (m_socketLineReader) {
|
if (m_socket) {
|
||||||
disconnect(m_socketLineReader->m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater);
|
disconnect(m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater);
|
||||||
delete m_socketLineReader;
|
delete m_socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_socketLineReader = new SocketLineReader(socket, this);
|
m_socket = socket;
|
||||||
|
socket->setParent(this);
|
||||||
|
|
||||||
connect(socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater);
|
connect(socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater);
|
||||||
connect(m_socketLineReader, &SocketLineReader::readyRead, this, &LanDeviceLink::dataReceived);
|
connect(socket, &QAbstractSocket::readyRead, this, &LanDeviceLink::dataReceived);
|
||||||
|
|
||||||
// We take ownership of the socket.
|
|
||||||
// When the link provider destroys us,
|
|
||||||
// the socket (and the reader) will be
|
|
||||||
// destroyed as well
|
|
||||||
socket->setParent(m_socketLineReader);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QHostAddress LanDeviceLink::hostAddress() const
|
QHostAddress LanDeviceLink::hostAddress() const
|
||||||
{
|
{
|
||||||
if (!m_socketLineReader) {
|
if (!m_socket) {
|
||||||
return QHostAddress::Null;
|
return QHostAddress::Null;
|
||||||
}
|
}
|
||||||
QHostAddress addr = m_socketLineReader->m_socket->peerAddress();
|
QHostAddress addr = m_socket->peerAddress();
|
||||||
if (addr.protocol() == QAbstractSocket::IPv6Protocol) {
|
if (addr.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||||
bool success;
|
bool success;
|
||||||
QHostAddress convertedAddr = QHostAddress(addr.toIPv4Address(&success));
|
QHostAddress convertedAddr = QHostAddress(addr.toIPv4Address(&success));
|
||||||
|
@ -80,7 +74,7 @@ bool LanDeviceLink::sendPacket(NetworkPacket &np)
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
int written = m_socketLineReader->write(np.serialize());
|
int written = m_socket->write(np.serialize());
|
||||||
|
|
||||||
// Actually we can't detect if a packet is received or not. We keep TCP
|
// Actually we can't detect if a packet is received or not. We keep TCP
|
||||||
//"ESTABLISHED" connections that look legit (return true when we use them),
|
//"ESTABLISHED" connections that look legit (return true when we use them),
|
||||||
|
@ -91,36 +85,31 @@ bool LanDeviceLink::sendPacket(NetworkPacket &np)
|
||||||
|
|
||||||
void LanDeviceLink::dataReceived()
|
void LanDeviceLink::dataReceived()
|
||||||
{
|
{
|
||||||
if (!m_socketLineReader->hasPacketsAvailable())
|
while (m_socket->canReadLine()) {
|
||||||
return;
|
const QByteArray serializedPacket = m_socket->readLine();
|
||||||
|
NetworkPacket packet;
|
||||||
|
NetworkPacket::unserialize(serializedPacket, &packet);
|
||||||
|
|
||||||
const QByteArray serializedPacket = m_socketLineReader->readLine();
|
// qCDebug(KDECONNECT_CORE) << "LanDeviceLink dataReceived" << serializedPacket;
|
||||||
NetworkPacket packet;
|
|
||||||
NetworkPacket::unserialize(serializedPacket, &packet);
|
|
||||||
|
|
||||||
// qCDebug(KDECONNECT_CORE) << "LanDeviceLink dataReceived" << serializedPacket;
|
if (packet.hasPayloadTransferInfo()) {
|
||||||
|
// qCDebug(KDECONNECT_CORE) << "HasPayloadTransferInfo";
|
||||||
|
const QVariantMap transferInfo = packet.payloadTransferInfo();
|
||||||
|
|
||||||
if (packet.hasPayloadTransferInfo()) {
|
QSharedPointer<QSslSocket> socket(new QSslSocket);
|
||||||
// qCDebug(KDECONNECT_CORE) << "HasPayloadTransferInfo";
|
|
||||||
const QVariantMap transferInfo = packet.payloadTransferInfo();
|
|
||||||
|
|
||||||
QSharedPointer<QSslSocket> socket(new QSslSocket);
|
LanLinkProvider::configureSslSocket(socket.data(), deviceId(), true);
|
||||||
|
|
||||||
LanLinkProvider::configureSslSocket(socket.data(), deviceId(), true);
|
// emit readChannelFinished when the socket gets disconnected. This seems to be a bug in upstream QSslSocket.
|
||||||
|
// Needs investigation and upstreaming of the fix. QTBUG-62257
|
||||||
|
connect(socket.data(), &QAbstractSocket::disconnected, socket.data(), &QAbstractSocket::readChannelFinished);
|
||||||
|
|
||||||
// emit readChannelFinished when the socket gets disconnected. This seems to be a bug in upstream QSslSocket.
|
const QString address = m_socket->peerAddress().toString();
|
||||||
// Needs investigation and upstreaming of the fix. QTBUG-62257
|
const quint16 port = transferInfo[QStringLiteral("port")].toInt();
|
||||||
connect(socket.data(), &QAbstractSocket::disconnected, socket.data(), &QAbstractSocket::readChannelFinished);
|
socket->connectToHostEncrypted(address, port, QIODevice::ReadWrite);
|
||||||
|
packet.setPayload(socket, packet.payloadSize());
|
||||||
|
}
|
||||||
|
|
||||||
const QString address = m_socketLineReader->peerAddress().toString();
|
Q_EMIT receivedPacket(packet);
|
||||||
const quint16 port = transferInfo[QStringLiteral("port")].toInt();
|
|
||||||
socket->connectToHostEncrypted(address, port, QIODevice::ReadWrite);
|
|
||||||
packet.setPayload(socket, packet.payloadSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_EMIT receivedPacket(packet);
|
|
||||||
|
|
||||||
if (m_socketLineReader->hasPacketsAvailable()) {
|
|
||||||
QMetaObject::invokeMethod(this, "dataReceived", Qt::QueuedConnection);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
#include "uploadjob.h"
|
#include "uploadjob.h"
|
||||||
#include <kdeconnectcore_export.h>
|
#include <kdeconnectcore_export.h>
|
||||||
|
|
||||||
class SocketLineReader;
|
|
||||||
class LanLinkProvider;
|
class LanLinkProvider;
|
||||||
|
|
||||||
class KDECONNECTCORE_EXPORT LanDeviceLink : public DeviceLink
|
class KDECONNECTCORE_EXPORT LanDeviceLink : public DeviceLink
|
||||||
|
@ -26,8 +25,6 @@ class KDECONNECTCORE_EXPORT LanDeviceLink : public DeviceLink
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum ConnectionStarted : bool { Locally, Remotely };
|
|
||||||
|
|
||||||
LanDeviceLink(const DeviceInfo &deviceInfo, LanLinkProvider *parent, QSslSocket *socket);
|
LanDeviceLink(const DeviceInfo &deviceInfo, LanLinkProvider *parent, QSslSocket *socket);
|
||||||
void reset(QSslSocket *socket);
|
void reset(QSslSocket *socket);
|
||||||
|
|
||||||
|
@ -44,8 +41,7 @@ private Q_SLOTS:
|
||||||
void dataReceived();
|
void dataReceived();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SocketLineReader *m_socketLineReader;
|
QSslSocket *m_socket;
|
||||||
ConnectionStarted m_connectionSource;
|
|
||||||
QPointer<CompositeUploadJob> m_compositeUploadJob;
|
QPointer<CompositeUploadJob> m_compositeUploadJob;
|
||||||
DeviceInfo m_deviceInfo;
|
DeviceInfo m_deviceInfo;
|
||||||
};
|
};
|
||||||
|
|
|
@ -427,7 +427,7 @@ void LanLinkProvider::newConnection()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// I'm the new device and this is the answer to my UDP identity packet (data received)
|
// I'm the new device and this is the TCP response to my UDP identity packet
|
||||||
void LanLinkProvider::dataReceived()
|
void LanLinkProvider::dataReceived()
|
||||||
{
|
{
|
||||||
QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
|
QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
|
||||||
|
@ -440,12 +440,10 @@ void LanLinkProvider::dataReceived()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
|
if (!socket->canReadLine()) {
|
||||||
if (!socket->canReadLine())
|
// This can happen if the packet is large enough to be split in two chunks
|
||||||
return;
|
return;
|
||||||
#else
|
}
|
||||||
socket->startTransaction();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const QByteArray data = socket->readLine();
|
const QByteArray data = socket->readLine();
|
||||||
|
|
||||||
|
@ -454,19 +452,10 @@ void LanLinkProvider::dataReceived()
|
||||||
NetworkPacket *np = new NetworkPacket();
|
NetworkPacket *np = new NetworkPacket();
|
||||||
bool success = NetworkPacket::unserialize(data, np);
|
bool success = NetworkPacket::unserialize(data, np);
|
||||||
|
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
delete np;
|
delete np;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
if (!success) {
|
|
||||||
delete np;
|
|
||||||
socket->rollbackTransaction();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
socket->commitTransaction();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (np->type() != PACKET_TYPE_IDENTITY) {
|
if (np->type() != PACKET_TYPE_IDENTITY) {
|
||||||
qCWarning(KDECONNECT_CORE) << "LanLinkProvider/newConnection: Expected identity, received " << np->type();
|
qCWarning(KDECONNECT_CORE) << "LanLinkProvider/newConnection: Expected identity, received " << np->type();
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
/**
|
|
||||||
* SPDX-FileCopyrightText: 2013 Albert Vaca <albertvaka@gmail.com>
|
|
||||||
* SPDX-FileCopyrightText: 2014 Alejandro Fiestas Olivares <afiestas@kde.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "socketlinereader.h"
|
|
||||||
|
|
||||||
SocketLineReader::SocketLineReader(QSslSocket *socket, QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
, m_socket(socket)
|
|
||||||
{
|
|
||||||
connect(m_socket, &QIODevice::readyRead, this, &SocketLineReader::dataReceived);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SocketLineReader::dataReceived()
|
|
||||||
{
|
|
||||||
while (m_socket->canReadLine()) {
|
|
||||||
const QByteArray line = m_socket->readLine();
|
|
||||||
if (line.length() > 1) { // we don't want a single \n
|
|
||||||
m_packets.enqueue(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have any packets, tell it to the world.
|
|
||||||
if (!m_packets.isEmpty()) {
|
|
||||||
Q_EMIT readyRead();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
/**
|
|
||||||
* SPDX-FileCopyrightText: 2013 Albert Vaca <albertvaka@gmail.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SOCKETLINEREADER_H
|
|
||||||
#define SOCKETLINEREADER_H
|
|
||||||
|
|
||||||
#include <QHostAddress>
|
|
||||||
#include <QObject>
|
|
||||||
#include <QQueue>
|
|
||||||
#include <QSslSocket>
|
|
||||||
|
|
||||||
#include <kdeconnectcore_export.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Encapsulates a QTcpSocket and implements the same methods of its API that are
|
|
||||||
* used by LanDeviceLink, but readyRead is emitted only when a newline is found.
|
|
||||||
*/
|
|
||||||
class KDECONNECTCORE_EXPORT SocketLineReader : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit SocketLineReader(QSslSocket *socket, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
bool hasPacketsAvailable() const
|
|
||||||
{
|
|
||||||
return !m_packets.isEmpty();
|
|
||||||
}
|
|
||||||
QByteArray readLine()
|
|
||||||
{
|
|
||||||
return m_packets.dequeue();
|
|
||||||
}
|
|
||||||
qint64 write(const QByteArray &data)
|
|
||||||
{
|
|
||||||
return m_socket->write(data);
|
|
||||||
}
|
|
||||||
QHostAddress peerAddress() const
|
|
||||||
{
|
|
||||||
return m_socket->peerAddress();
|
|
||||||
}
|
|
||||||
QSslCertificate peerCertificate() const
|
|
||||||
{
|
|
||||||
return m_socket->peerCertificate();
|
|
||||||
}
|
|
||||||
|
|
||||||
QSslSocket *m_socket;
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void readyRead();
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void dataReceived();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QByteArray m_lastChunk;
|
|
||||||
QQueue<QByteArray> m_packets;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -15,4 +15,3 @@ endif()
|
||||||
|
|
||||||
ecm_add_test(pluginloadtest.cpp LINK_LIBRARIES ${kdeconnect_libraries})
|
ecm_add_test(pluginloadtest.cpp LINK_LIBRARIES ${kdeconnect_libraries})
|
||||||
ecm_add_test(sendfiletest.cpp LINK_LIBRARIES ${kdeconnect_libraries})
|
ecm_add_test(sendfiletest.cpp LINK_LIBRARIES ${kdeconnect_libraries})
|
||||||
ecm_add_test(testsocketlinereader.cpp TEST_NAME testsocketlinereader LINK_LIBRARIES ${kdeconnect_libraries})
|
|
||||||
|
|
|
@ -1,141 +0,0 @@
|
||||||
/*************************************************************************************
|
|
||||||
* SPDX-FileCopyrightText: 2014 Alejandro Fiestas Olivares <afiestas@kde.org> *
|
|
||||||
* *
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*************************************************************************************/
|
|
||||||
|
|
||||||
#include "../core/backends/lan/server.h"
|
|
||||||
#include "../core/backends/lan/socketlinereader.h"
|
|
||||||
#include "../core/qtcompat_p.h"
|
|
||||||
|
|
||||||
#include <QEventLoop>
|
|
||||||
#include <QProcess>
|
|
||||||
#include <QSignalSpy>
|
|
||||||
#include <QSslSocket>
|
|
||||||
#include <QTest>
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
class TestSocketLineReader : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public Q_SLOTS:
|
|
||||||
void init();
|
|
||||||
void cleanup()
|
|
||||||
{
|
|
||||||
delete m_server;
|
|
||||||
}
|
|
||||||
void newPacket();
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void socketLineReader();
|
|
||||||
void badData();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QTimer m_timer;
|
|
||||||
QEventLoop m_loop;
|
|
||||||
QList<QByteArray> m_packets;
|
|
||||||
Server *m_server;
|
|
||||||
QSslSocket *m_conn;
|
|
||||||
SocketLineReader *m_reader;
|
|
||||||
};
|
|
||||||
|
|
||||||
void TestSocketLineReader::init()
|
|
||||||
{
|
|
||||||
m_packets.clear();
|
|
||||||
m_server = new Server(this);
|
|
||||||
|
|
||||||
QVERIFY2(m_server->listen(QHostAddress::LocalHost, 8694), "Failed to create local tcp server");
|
|
||||||
|
|
||||||
m_timer.setInterval(4000); // For second is more enough to send some data via local socket
|
|
||||||
m_timer.setSingleShot(true);
|
|
||||||
connect(&m_timer, &QTimer::timeout, &m_loop, &QEventLoop::quit);
|
|
||||||
|
|
||||||
m_conn = new QSslSocket(this);
|
|
||||||
m_conn->connectToHost(QHostAddress::LocalHost, 8694);
|
|
||||||
connect(m_conn, &QAbstractSocket::connected, &m_loop, &QEventLoop::quit);
|
|
||||||
m_timer.start();
|
|
||||||
m_loop.exec();
|
|
||||||
|
|
||||||
QVERIFY2(m_conn->isOpen(), "Could not connect to local tcp server");
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestSocketLineReader::socketLineReader()
|
|
||||||
{
|
|
||||||
QList<QByteArray> dataToSend;
|
|
||||||
dataToSend << "foobar\n"
|
|
||||||
<< "barfoo\n"
|
|
||||||
<< "foobar?\n"
|
|
||||||
<< "\n"
|
|
||||||
<< "barfoo!\n"
|
|
||||||
<< "panda\n";
|
|
||||||
for (const QByteArray &line : qAsConst(dataToSend)) {
|
|
||||||
m_conn->write(line);
|
|
||||||
}
|
|
||||||
m_conn->flush();
|
|
||||||
|
|
||||||
int maxAttemps = 5;
|
|
||||||
while (!m_server->hasPendingConnections() && maxAttemps > 0) {
|
|
||||||
--maxAttemps;
|
|
||||||
QTest::qSleep(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
QSslSocket *sock = m_server->nextPendingConnection();
|
|
||||||
|
|
||||||
QVERIFY2(sock != nullptr, "Could not open a connection to the client");
|
|
||||||
|
|
||||||
m_reader = new SocketLineReader(sock, this);
|
|
||||||
connect(m_reader, &SocketLineReader::readyRead, this, &TestSocketLineReader::newPacket);
|
|
||||||
m_timer.start();
|
|
||||||
m_loop.exec();
|
|
||||||
|
|
||||||
/* remove the empty line before compare */
|
|
||||||
dataToSend.removeOne("\n");
|
|
||||||
|
|
||||||
QCOMPARE(m_packets.count(), 5); // We expect 5 Packets
|
|
||||||
for (int x = 0; x < 5; ++x) {
|
|
||||||
QCOMPARE(m_packets[x], dataToSend[x]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestSocketLineReader::badData()
|
|
||||||
{
|
|
||||||
const QList<QByteArray> dataToSend = {"data1\n", "data"}; // does not end in a \n
|
|
||||||
for (const QByteArray &line : qAsConst(dataToSend)) {
|
|
||||||
m_conn->write(line);
|
|
||||||
}
|
|
||||||
m_conn->flush();
|
|
||||||
|
|
||||||
QSignalSpy spy(m_server, &QTcpServer::newConnection);
|
|
||||||
QVERIFY(m_server->hasPendingConnections() || spy.wait(1000));
|
|
||||||
QSslSocket *sock = m_server->nextPendingConnection();
|
|
||||||
|
|
||||||
QVERIFY2(sock != nullptr, "Could not open a connection to the client");
|
|
||||||
|
|
||||||
m_reader = new SocketLineReader(sock, this);
|
|
||||||
connect(m_reader, &SocketLineReader::readyRead, this, &TestSocketLineReader::newPacket);
|
|
||||||
m_timer.start();
|
|
||||||
m_loop.exec();
|
|
||||||
|
|
||||||
QCOMPARE(m_packets.count(), 1);
|
|
||||||
QCOMPARE(m_packets[0], dataToSend[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestSocketLineReader::newPacket()
|
|
||||||
{
|
|
||||||
int maxLoops = 5;
|
|
||||||
while (m_reader->hasPacketsAvailable() && maxLoops > 0) {
|
|
||||||
--maxLoops;
|
|
||||||
const QByteArray packet = m_reader->readLine();
|
|
||||||
if (!packet.isEmpty()) {
|
|
||||||
m_packets.append(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_packets.count() == 5) {
|
|
||||||
m_loop.exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QTEST_GUILESS_MAIN(TestSocketLineReader)
|
|
||||||
|
|
||||||
#include "testsocketlinereader.moc"
|
|
Loading…
Reference in a new issue