From 5c05f5df1f35291070962b7fe295004e4942f2ce Mon Sep 17 00:00:00 2001 From: Aurindam Jana Date: Tue, 6 Mar 2012 23:35:27 +0100 Subject: QmlDebuggingTests: Separate out public and private tests Remove dependencies on quick-private from all possible debugging auto tests and list them under public tests. Change-Id: I688b5b36fdf3d6fbcb6cef2a975ecd1bf679af2b Reviewed-by: Kai Koehne --- src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro | 8 +- .../qmltooling/qmldbg_tcp/qtcpserverconnection.cpp | 2 +- src/plugins/qmltooling/shared/qpacketprotocol.cpp | 550 +++++++++++++++++++++ src/plugins/qmltooling/shared/qpacketprotocol.h | 119 +++++ 4 files changed, 676 insertions(+), 3 deletions(-) create mode 100644 src/plugins/qmltooling/shared/qpacketprotocol.cpp create mode 100644 src/plugins/qmltooling/shared/qpacketprotocol.h (limited to 'src/plugins') diff --git a/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro b/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro index 0c5e196b18..fdd1f6e05f 100644 --- a/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro +++ b/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro @@ -9,10 +9,14 @@ DESTDIR = $$QT.qml.plugins/qmltooling QTDIR_build:REQUIRES += "contains(QT_CONFIG, qml)" SOURCES += \ - qtcpserverconnection.cpp + qtcpserverconnection.cpp \ + ../shared/qpacketprotocol.cpp HEADERS += \ - qtcpserverconnection.h + qtcpserverconnection.h \ + ../shared/qpacketprotocol.h + +INCLUDEPATH += ../shared OTHER_FILES += qtcpserverconnection.json diff --git a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp index 4eccd0dd55..e5f31b6f5c 100644 --- a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp +++ b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp @@ -40,13 +40,13 @@ ****************************************************************************/ #include "qtcpserverconnection.h" +#include "qpacketprotocol.h" #include #include #include #include -#include QT_BEGIN_NAMESPACE diff --git a/src/plugins/qmltooling/shared/qpacketprotocol.cpp b/src/plugins/qmltooling/shared/qpacketprotocol.cpp new file mode 100644 index 0000000000..a6a4e0e886 --- /dev/null +++ b/src/plugins/qmltooling/shared/qpacketprotocol.cpp @@ -0,0 +1,550 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpacketprotocol.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +static const unsigned int MAX_PACKET_SIZE = 0x7FFFFFFF; + +/*! + \class QPacketProtocol + \internal + + \brief The QPacketProtocol class encapsulates communicating discrete packets + across fragmented IO channels, such as TCP sockets. + + QPacketProtocol makes it simple to send arbitrary sized data "packets" across + fragmented transports such as TCP and UDP. + + As transmission boundaries are not respected, sending packets over protocols + like TCP frequently involves "stitching" them back together at the receiver. + QPacketProtocol makes this easier by performing this task for you. Packet + data sent using QPacketProtocol is prepended with a 4-byte size header + allowing the receiving QPacketProtocol to buffer the packet internally until + it has all been received. QPacketProtocol does not perform any sanity + checking on the size or on the data, so this class should only be used in + prototyping or trusted situations where DOS attacks are unlikely. + + QPacketProtocol does not perform any communications itself. Instead it can + operate on any QIODevice that supports the QIODevice::readyRead() signal. A + logical "packet" is encapsulated by the companion QPacket class. The + following example shows two ways to send data using QPacketProtocol. The + transmitted data is equivalent in both. + + \code + QTcpSocket socket; + // ... connect socket ... + + QPacketProtocol protocol(&socket); + + // Send packet the quick way + protocol.send() << "Hello world" << 123; + + // Send packet the longer way + QPacket packet; + packet << "Hello world" << 123; + protocol.send(packet); + \endcode + + Likewise, the following shows how to read data from QPacketProtocol, assuming + that the QPacketProtocol::readyRead() signal has been emitted. + + \code + // ... QPacketProtocol::readyRead() is emitted ... + + int a; + QByteArray b; + + // Receive packet the quick way + protocol.read() >> a >> b; + + // Receive packet the longer way + QPacket packet = protocol.read(); + p >> a >> b; + \endcode + + \ingroup io + \sa QPacket +*/ + +class QPacketProtocolPrivate : public QObject +{ + Q_OBJECT +public: + QPacketProtocolPrivate(QPacketProtocol *parent, QIODevice *_dev) + : QObject(parent), inProgressSize(-1), maxPacketSize(MAX_PACKET_SIZE), + waitingForPacket(false), dev(_dev) + { + Q_ASSERT(4 == sizeof(qint32)); + + QObject::connect(this, SIGNAL(readyRead()), + parent, SIGNAL(readyRead())); + QObject::connect(this, SIGNAL(packetWritten()), + parent, SIGNAL(packetWritten())); + QObject::connect(this, SIGNAL(invalidPacket()), + parent, SIGNAL(invalidPacket())); + QObject::connect(dev, SIGNAL(readyRead()), + this, SLOT(readyToRead())); + QObject::connect(dev, SIGNAL(aboutToClose()), + this, SLOT(aboutToClose())); + QObject::connect(dev, SIGNAL(bytesWritten(qint64)), + this, SLOT(bytesWritten(qint64))); + } + +Q_SIGNALS: + void readyRead(); + void packetWritten(); + void invalidPacket(); + +public Q_SLOTS: + void aboutToClose() + { + inProgress.clear(); + sendingPackets.clear(); + inProgressSize = -1; + } + + void bytesWritten(qint64 bytes) + { + Q_ASSERT(!sendingPackets.isEmpty()); + + while (bytes) { + if (sendingPackets.at(0) > bytes) { + sendingPackets[0] -= bytes; + bytes = 0; + } else { + bytes -= sendingPackets.at(0); + sendingPackets.removeFirst(); + emit packetWritten(); + } + } + } + + void readyToRead() + { + while (true) { + // Need to get trailing data + if (-1 == inProgressSize) { + // We need a size header of sizeof(qint32) + if (sizeof(qint32) > (uint)dev->bytesAvailable()) + return; + + // Read size header + int read = dev->read((char *)&inProgressSize, sizeof(qint32)); + Q_ASSERT(read == sizeof(qint32)); + Q_UNUSED(read); + + // Check sizing constraints + if (inProgressSize > maxPacketSize) { + QObject::disconnect(dev, SIGNAL(readyRead()), + this, SLOT(readyToRead())); + QObject::disconnect(dev, SIGNAL(aboutToClose()), + this, SLOT(aboutToClose())); + QObject::disconnect(dev, SIGNAL(bytesWritten(qint64)), + this, SLOT(bytesWritten(qint64))); + dev = 0; + emit invalidPacket(); + return; + } + + inProgressSize -= sizeof(qint32); + } else { + inProgress.append(dev->read(inProgressSize - inProgress.size())); + + if (inProgressSize == inProgress.size()) { + // Packet has arrived! + packets.append(inProgress); + inProgressSize = -1; + inProgress.clear(); + + waitingForPacket = false; + emit readyRead(); + } else + return; + } + } + } + +public: + QList sendingPackets; + QList packets; + QByteArray inProgress; + qint32 inProgressSize; + qint32 maxPacketSize; + bool waitingForPacket; + QIODevice *dev; +}; + +/*! + Construct a QPacketProtocol instance that works on \a dev with the + specified \a parent. + */ +QPacketProtocol::QPacketProtocol(QIODevice *dev, QObject *parent) + : QObject(parent), d(new QPacketProtocolPrivate(this, dev)) +{ + Q_ASSERT(dev); +} + +/*! + Destroys the QPacketProtocol instance. + */ +QPacketProtocol::~QPacketProtocol() +{ +} + +/*! + Returns the maximum packet size allowed. By default this is + 2,147,483,647 bytes. + + If a packet claiming to be larger than the maximum packet size is received, + the QPacketProtocol::invalidPacket() signal is emitted. + + \sa QPacketProtocol::setMaximumPacketSize() + */ +qint32 QPacketProtocol::maximumPacketSize() const +{ + return d->maxPacketSize; +} + +/*! + Sets the maximum allowable packet size to \a max. + + \sa QPacketProtocol::maximumPacketSize() + */ +qint32 QPacketProtocol::setMaximumPacketSize(qint32 max) +{ + if (max > (signed)sizeof(qint32)) + d->maxPacketSize = max; + return d->maxPacketSize; +} + +/*! + Returns a streamable object that is transmitted on destruction. For example + + \code + protocol.send() << "Hello world" << 123; + \endcode + + will send a packet containing "Hello world" and 123. To construct more + complex packets, explicitly construct a QPacket instance. + */ +QPacketAutoSend QPacketProtocol::send() +{ + return QPacketAutoSend(this); +} + +/*! + \fn void QPacketProtocol::send(const QPacket & packet) + + Transmit the \a packet. + */ +void QPacketProtocol::send(const QPacket & p) +{ + if (p.b.isEmpty()) + return; // We don't send empty packets + + qint64 sendSize = p.b.size() + sizeof(qint32); + + d->sendingPackets.append(sendSize); + qint32 sendSize32 = sendSize; + qint64 writeBytes = d->dev->write((char *)&sendSize32, sizeof(qint32)); + Q_ASSERT(writeBytes == sizeof(qint32)); + writeBytes = d->dev->write(p.b); + Q_ASSERT(writeBytes == p.b.size()); +} + +/*! + Returns the number of received packets yet to be read. + */ +qint64 QPacketProtocol::packetsAvailable() const +{ + return d->packets.count(); +} + +/*! + Discard any unread packets. + */ +void QPacketProtocol::clear() +{ + d->packets.clear(); +} + +/*! + Return the next unread packet, or an invalid QPacket instance if no packets + are available. This method does NOT block. + */ +QPacket QPacketProtocol::read() +{ + if (0 == d->packets.count()) + return QPacket(); + + QPacket rv(d->packets.at(0)); + d->packets.removeFirst(); + return rv; +} + +/* + Returns the difference between msecs and elapsed. If msecs is -1, + however, -1 is returned. +*/ +static int qt_timeout_value(int msecs, int elapsed) +{ + if (msecs == -1) + return -1; + + int timeout = msecs - elapsed; + return timeout < 0 ? 0 : timeout; +} + +/*! + This function locks until a new packet is available for reading and the + \l{QIODevice::}{readyRead()} signal has been emitted. The function + will timeout after \a msecs milliseconds; the default timeout is + 30000 milliseconds. + + The function returns true if the readyRead() signal is emitted and + there is new data available for reading; otherwise it returns false + (if an error occurred or the operation timed out). + */ + +bool QPacketProtocol::waitForReadyRead(int msecs) +{ + if (!d->packets.isEmpty()) + return true; + + QElapsedTimer stopWatch; + stopWatch.start(); + + d->waitingForPacket = true; + do { + if (!d->dev->waitForReadyRead(msecs)) + return false; + if (!d->waitingForPacket) + return true; + msecs = qt_timeout_value(msecs, stopWatch.elapsed()); + } while (true); +} + +/*! + Return the QIODevice passed to the QPacketProtocol constructor. +*/ +QIODevice *QPacketProtocol::device() +{ + return d->dev; +} + +/*! + \fn void QPacketProtocol::readyRead() + + Emitted whenever a new packet is received. Applications may use + QPacketProtocol::read() to retrieve this packet. + */ + +/*! + \fn void QPacketProtocol::invalidPacket() + + A packet larger than the maximum allowable packet size was received. The + packet will be discarded and, as it indicates corruption in the protocol, no + further packets will be received. + */ + +/*! + \fn void QPacketProtocol::packetWritten() + + Emitted each time a packet is completing written to the device. This signal + may be used for communications flow control. + */ + +/*! + \class QPacket + \internal + + \brief The QPacket class encapsulates an unfragmentable packet of data to be + transmitted by QPacketProtocol. + + The QPacket class works together with QPacketProtocol to make it simple to + send arbitrary sized data "packets" across fragmented transports such as TCP + and UDP. + + QPacket provides a QDataStream interface to an unfragmentable packet. + Applications should construct a QPacket, propagate it with data and then + transmit it over a QPacketProtocol instance. For example: + \code + QPacketProtocol protocol(...); + + QPacket myPacket; + myPacket << "Hello world!" << 123; + protocol.send(myPacket); + \endcode + + As long as both ends of the connection are using the QPacketProtocol class, + the data within this packet will be delivered unfragmented at the other end, + ready for extraction. + + \code + QByteArray greeting; + int count; + + QPacket myPacket = protocol.read(); + + myPacket >> greeting >> count; + \endcode + + Only packets returned from QPacketProtocol::read() may be read from. QPacket + instances constructed by directly by applications are for transmission only + and are considered "write only". Attempting to read data from them will + result in undefined behavior. + + \ingroup io + \sa QPacketProtocol + */ + +/*! + Constructs an empty write-only packet. + */ +QPacket::QPacket() + : QDataStream(), buf(0) +{ + buf = new QBuffer(&b); + buf->open(QIODevice::WriteOnly); + setDevice(buf); + setVersion(QDataStream::Qt_4_7); +} + +/*! + Destroys the QPacket instance. + */ +QPacket::~QPacket() +{ + if (buf) { + delete buf; + buf = 0; + } +} + +/*! + Creates a copy of \a other. The initial stream positions are shared, but the + two packets are otherwise independent. + */ +QPacket::QPacket(const QPacket & other) + : QDataStream(), b(other.b), buf(0) +{ + buf = new QBuffer(&b); + buf->open(other.buf->openMode()); + setDevice(buf); +} + +/*! + \internal + */ +QPacket::QPacket(const QByteArray & ba) + : QDataStream(), b(ba), buf(0) +{ + buf = new QBuffer(&b); + buf->open(QIODevice::ReadOnly); + setDevice(buf); +} + +/*! + Returns true if this packet is empty - that is, contains no data. + */ +bool QPacket::isEmpty() const +{ + return b.isEmpty(); +} + +/*! + Returns raw packet data. + */ +QByteArray QPacket::data() const +{ + return b; +} + +/*! + Clears data in the packet. This is useful for reusing one writable packet. + For example + \code + QPacketProtocol protocol(...); + + QPacket packet; + + packet << "Hello world!" << 123; + protocol.send(packet); + + packet.clear(); + packet << "Goodbyte world!" << 789; + protocol.send(packet); + \endcode + */ +void QPacket::clear() +{ + QBuffer::OpenMode oldMode = buf->openMode(); + buf->close(); + b.clear(); + buf->setBuffer(&b); // reset QBuffer internals with new size of b. + buf->open(oldMode); +} + +/*! + \class QPacketAutoSend + \internal + + \internal + */ +QPacketAutoSend::QPacketAutoSend(QPacketProtocol *_p) + : QPacket(), p(_p) +{ +} + +QPacketAutoSend::~QPacketAutoSend() +{ + if (!b.isEmpty()) + p->send(*this); +} + +QT_END_NAMESPACE + +#include diff --git a/src/plugins/qmltooling/shared/qpacketprotocol.h b/src/plugins/qmltooling/shared/qpacketprotocol.h new file mode 100644 index 0000000000..0470c74643 --- /dev/null +++ b/src/plugins/qmltooling/shared/qpacketprotocol.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPACKETPROTOCOL_H +#define QPACKETPROTOCOL_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QIODevice; +class QBuffer; +class QPacket; +class QPacketAutoSend; +class QPacketProtocolPrivate; + +class QPacketProtocol : public QObject +{ + Q_OBJECT +public: + explicit QPacketProtocol(QIODevice *dev, QObject *parent = 0); + virtual ~QPacketProtocol(); + + qint32 maximumPacketSize() const; + qint32 setMaximumPacketSize(qint32); + + QPacketAutoSend send(); + void send(const QPacket &); + + qint64 packetsAvailable() const; + QPacket read(); + + bool waitForReadyRead(int msecs = 3000); + + void clear(); + + QIODevice *device(); + +Q_SIGNALS: + void readyRead(); + void invalidPacket(); + void packetWritten(); + +private: + QPacketProtocolPrivate *d; +}; + + +class QPacket : public QDataStream +{ +public: + QPacket(); + QPacket(const QPacket &); + virtual ~QPacket(); + + void clear(); + bool isEmpty() const; + QByteArray data() const; + +protected: + friend class QPacketProtocol; + QPacket(const QByteArray &ba); + QByteArray b; + QBuffer *buf; +}; + +class QPacketAutoSend : public QPacket +{ +public: + virtual ~QPacketAutoSend(); + +private: + friend class QPacketProtocol; + QPacketAutoSend(QPacketProtocol *); + QPacketProtocol *p; +}; + +QT_END_NAMESPACE + +#endif -- cgit v1.2.3