Absolute File Name: | /home/opencoverage/opencoverage/guest-scripts/qtdeclarative/src/qtdeclarative/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp |
Source code | Switch to Preprocessed file |
Line | Source | Count | ||||||
---|---|---|---|---|---|---|---|---|
1 | /**************************************************************************** | - | ||||||
2 | ** | - | ||||||
3 | ** Copyright (C) 2016 The Qt Company Ltd. | - | ||||||
4 | ** Contact: https://www.qt.io/licensing/ | - | ||||||
5 | ** | - | ||||||
6 | ** This file is part of the QtQml module of the Qt Toolkit. | - | ||||||
7 | ** | - | ||||||
8 | ** $QT_BEGIN_LICENSE:LGPL$ | - | ||||||
9 | ** Commercial License Usage | - | ||||||
10 | ** Licensees holding valid commercial Qt licenses may use this file in | - | ||||||
11 | ** accordance with the commercial license agreement provided with the | - | ||||||
12 | ** Software or, alternatively, in accordance with the terms contained in | - | ||||||
13 | ** a written agreement between you and The Qt Company. For licensing terms | - | ||||||
14 | ** and conditions see https://www.qt.io/terms-conditions. For further | - | ||||||
15 | ** information use the contact form at https://www.qt.io/contact-us. | - | ||||||
16 | ** | - | ||||||
17 | ** GNU Lesser General Public License Usage | - | ||||||
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser | - | ||||||
19 | ** General Public License version 3 as published by the Free Software | - | ||||||
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the | - | ||||||
21 | ** packaging of this file. Please review the following information to | - | ||||||
22 | ** ensure the GNU Lesser General Public License version 3 requirements | - | ||||||
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. | - | ||||||
24 | ** | - | ||||||
25 | ** GNU General Public License Usage | - | ||||||
26 | ** Alternatively, this file may be used under the terms of the GNU | - | ||||||
27 | ** General Public License version 2.0 or (at your option) the GNU General | - | ||||||
28 | ** Public license version 3 or any later version approved by the KDE Free | - | ||||||
29 | ** Qt Foundation. The licenses are as published by the Free Software | - | ||||||
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 | - | ||||||
31 | ** included in the packaging of this file. Please review the following | - | ||||||
32 | ** information to ensure the GNU General Public License requirements will | - | ||||||
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and | - | ||||||
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. | - | ||||||
35 | ** | - | ||||||
36 | ** $QT_END_LICENSE$ | - | ||||||
37 | ** | - | ||||||
38 | ****************************************************************************/ | - | ||||||
39 | - | |||||||
40 | #include "qpacketprotocol_p.h" | - | ||||||
41 | - | |||||||
42 | #include <QtCore/QElapsedTimer> | - | ||||||
43 | #include <QtCore/QtEndian> | - | ||||||
44 | - | |||||||
45 | #include <private/qiodevice_p.h> | - | ||||||
46 | #include <private/qobject_p.h> | - | ||||||
47 | - | |||||||
48 | QT_BEGIN_NAMESPACE | - | ||||||
49 | - | |||||||
50 | /*! | - | ||||||
51 | \class QPacketProtocol | - | ||||||
52 | \internal | - | ||||||
53 | - | |||||||
54 | \brief The QPacketProtocol class encapsulates communicating discrete packets | - | ||||||
55 | across fragmented IO channels, such as TCP sockets. | - | ||||||
56 | - | |||||||
57 | QPacketProtocol makes it simple to send arbitrary sized data "packets" across | - | ||||||
58 | fragmented transports such as TCP and UDP. | - | ||||||
59 | - | |||||||
60 | As transmission boundaries are not respected, sending packets over protocols | - | ||||||
61 | like TCP frequently involves "stitching" them back together at the receiver. | - | ||||||
62 | QPacketProtocol makes this easier by performing this task for you. Packet | - | ||||||
63 | data sent using QPacketProtocol is prepended with a 4-byte size header | - | ||||||
64 | allowing the receiving QPacketProtocol to buffer the packet internally until | - | ||||||
65 | it has all been received. QPacketProtocol does not perform any sanity | - | ||||||
66 | checking on the size or on the data, so this class should only be used in | - | ||||||
67 | prototyping or trusted situations where DOS attacks are unlikely. | - | ||||||
68 | - | |||||||
69 | QPacketProtocol does not perform any communications itself. Instead it can | - | ||||||
70 | operate on any QIODevice that supports the QIODevice::readyRead() signal. A | - | ||||||
71 | logical "packet" is simply a QByteArray. The following example how to send | - | ||||||
72 | data using QPacketProtocol. | - | ||||||
73 | - | |||||||
74 | \code | - | ||||||
75 | QTcpSocket socket; | - | ||||||
76 | // ... connect socket ... | - | ||||||
77 | - | |||||||
78 | QPacketProtocol protocol(&socket); | - | ||||||
79 | - | |||||||
80 | // Send a packet | - | ||||||
81 | QDataStream packet; | - | ||||||
82 | packet << "Hello world" << 123; | - | ||||||
83 | protocol.send(packet.data()); | - | ||||||
84 | \endcode | - | ||||||
85 | - | |||||||
86 | Likewise, the following shows how to read data from QPacketProtocol, assuming | - | ||||||
87 | that the QPacketProtocol::readyRead() signal has been emitted. | - | ||||||
88 | - | |||||||
89 | \code | - | ||||||
90 | // ... QPacketProtocol::readyRead() is emitted ... | - | ||||||
91 | - | |||||||
92 | int a; | - | ||||||
93 | QByteArray b; | - | ||||||
94 | - | |||||||
95 | // Receive packet | - | ||||||
96 | QDataStream packet(protocol.read()); | - | ||||||
97 | p >> a >> b; | - | ||||||
98 | \endcode | - | ||||||
99 | - | |||||||
100 | \ingroup io | - | ||||||
101 | */ | - | ||||||
102 | - | |||||||
103 | class QPacketProtocolPrivate : public QObjectPrivate | - | ||||||
104 | { | - | ||||||
105 | Q_DECLARE_PUBLIC(QPacketProtocol) | - | ||||||
106 | public: | - | ||||||
107 | QPacketProtocolPrivate(QIODevice *dev); | - | ||||||
108 | - | |||||||
109 | bool writeToDevice(const char *bytes, qint64 size); | - | ||||||
110 | bool readFromDevice(char *buffer, qint64 size); | - | ||||||
111 | - | |||||||
112 | QList<qint32> sendingPackets; | - | ||||||
113 | QList<QByteArray> packets; | - | ||||||
114 | QByteArray inProgress; | - | ||||||
115 | qint32 inProgressSize; | - | ||||||
116 | bool waitingForPacket; | - | ||||||
117 | QIODevice *dev; | - | ||||||
118 | }; | - | ||||||
119 | - | |||||||
120 | /*! | - | ||||||
121 | Construct a QPacketProtocol instance that works on \a dev with the | - | ||||||
122 | specified \a parent. | - | ||||||
123 | */ | - | ||||||
124 | QPacketProtocol::QPacketProtocol(QIODevice *dev, QObject *parent) | - | ||||||
125 | : QObject(*(new QPacketProtocolPrivate(dev)), parent) | - | ||||||
126 | { | - | ||||||
127 | Q_ASSERT(4 == sizeof(qint32)); | - | ||||||
128 | Q_ASSERT(dev); | - | ||||||
129 | - | |||||||
130 | QObject::connect(dev, &QIODevice::readyRead, this, &QPacketProtocol::readyToRead); | - | ||||||
131 | QObject::connect(dev, &QIODevice::aboutToClose, this, &QPacketProtocol::aboutToClose); | - | ||||||
132 | QObject::connect(dev, &QIODevice::bytesWritten, this, &QPacketProtocol::bytesWritten); | - | ||||||
133 | } executed 601 times by 12 tests: end of block Executed by:
| 601 | ||||||
134 | - | |||||||
135 | /*! | - | ||||||
136 | \fn void QPacketProtocol::send(const QByteArray &data) | - | ||||||
137 | - | |||||||
138 | Transmit the \a packet. | - | ||||||
139 | */ | - | ||||||
140 | void QPacketProtocol::send(const QByteArray &data) | - | ||||||
141 | { | - | ||||||
142 | Q_D(QPacketProtocol); | - | ||||||
143 | static const qint32 maxSize = std::numeric_limits<qint32>::max() - sizeof(qint32); | - | ||||||
144 | - | |||||||
145 | if (data.isEmpty())
| 0-6289 | ||||||
146 | return; // We don't send empty packets never executed: return; | 0 | ||||||
147 | - | |||||||
148 | if (data.size() > maxSize) {
| 0-6289 | ||||||
149 | emit error(); | - | ||||||
150 | return; never executed: return; | 0 | ||||||
151 | } | - | ||||||
152 | - | |||||||
153 | const qint32 sendSize = data.size() + static_cast<qint32>(sizeof(qint32)); | - | ||||||
154 | d->sendingPackets.append(sendSize); | - | ||||||
155 | - | |||||||
156 | qint32 sendSizeLE = qToLittleEndian(sendSize); | - | ||||||
157 | if (!d->writeToDevice((const char *)&sendSizeLE, sizeof(qint32))
| 0-6289 | ||||||
158 | || !d->writeToDevice(data.data(), data.size())) {
| 0-6289 | ||||||
159 | emit error(); | - | ||||||
160 | } never executed: end of block | 0 | ||||||
161 | } executed 6289 times by 12 tests: end of block Executed by:
| 6289 | ||||||
162 | - | |||||||
163 | /*! | - | ||||||
164 | Returns the number of received packets yet to be read. | - | ||||||
165 | */ | - | ||||||
166 | qint64 QPacketProtocol::packetsAvailable() const | - | ||||||
167 | { | - | ||||||
168 | Q_D(const QPacketProtocol); | - | ||||||
169 | return d->packets.count(); executed 15350 times by 12 tests: return d->packets.count(); Executed by:
| 15350 | ||||||
170 | } | - | ||||||
171 | - | |||||||
172 | /*! | - | ||||||
173 | Return the next unread packet, or an empty QByteArray if no packets | - | ||||||
174 | are available. This method does NOT block. | - | ||||||
175 | */ | - | ||||||
176 | QByteArray QPacketProtocol::read() | - | ||||||
177 | { | - | ||||||
178 | Q_D(QPacketProtocol); | - | ||||||
179 | return d->packets.isEmpty() ? QByteArray() : d->packets.takeFirst(); executed 8660 times by 12 tests: return d->packets.isEmpty() ? QByteArray() : d->packets.takeFirst(); Executed by:
| 8660 | ||||||
180 | } | - | ||||||
181 | - | |||||||
182 | /*! | - | ||||||
183 | This function locks until a new packet is available for reading and the | - | ||||||
184 | \l{QIODevice::}{readyRead()} signal has been emitted. The function | - | ||||||
185 | will timeout after \a msecs milliseconds; the default timeout is | - | ||||||
186 | 30000 milliseconds. | - | ||||||
187 | - | |||||||
188 | The function returns true if the readyRead() signal is emitted and | - | ||||||
189 | there is new data available for reading; otherwise it returns false | - | ||||||
190 | (if an error occurred or the operation timed out). | - | ||||||
191 | */ | - | ||||||
192 | - | |||||||
193 | bool QPacketProtocol::waitForReadyRead(int msecs) | - | ||||||
194 | { | - | ||||||
195 | Q_D(QPacketProtocol); | - | ||||||
196 | if (!d->packets.isEmpty())
| 0-22 | ||||||
197 | return true; never executed: return true; | 0 | ||||||
198 | - | |||||||
199 | QElapsedTimer stopWatch; | - | ||||||
200 | stopWatch.start(); | - | ||||||
201 | - | |||||||
202 | d->waitingForPacket = true; | - | ||||||
203 | do { | - | ||||||
204 | if (!d->dev->waitForReadyRead(msecs))
| 0-22 | ||||||
205 | return false; never executed: return false; | 0 | ||||||
206 | if (!d->waitingForPacket)
| 0-22 | ||||||
207 | return true; executed 22 times by 2 tests: return true; Executed by:
| 22 | ||||||
208 | msecs = qt_subtract_from_timeout(msecs, stopWatch.elapsed()); | - | ||||||
209 | } while (true); never executed: end of block | 0 | ||||||
210 | } never executed: end of block | 0 | ||||||
211 | - | |||||||
212 | /*! | - | ||||||
213 | Return the QIODevice passed to the QPacketProtocol constructor. | - | ||||||
214 | */ | - | ||||||
215 | void QPacketProtocol::aboutToClose() | - | ||||||
216 | { | - | ||||||
217 | Q_D(QPacketProtocol); | - | ||||||
218 | d->inProgress.clear(); | - | ||||||
219 | d->sendingPackets.clear(); | - | ||||||
220 | d->inProgressSize = -1; | - | ||||||
221 | } executed 566 times by 11 tests: end of block Executed by:
| 566 | ||||||
222 | - | |||||||
223 | void QPacketProtocol::bytesWritten(qint64 bytes) | - | ||||||
224 | { | - | ||||||
225 | Q_D(QPacketProtocol); | - | ||||||
226 | Q_ASSERT(!d->sendingPackets.isEmpty()); | - | ||||||
227 | - | |||||||
228 | while (bytes) {
| 6267-6289 | ||||||
229 | if (d->sendingPackets.at(0) > bytes) {
| 0-6289 | ||||||
230 | d->sendingPackets[0] -= bytes; | - | ||||||
231 | bytes = 0; | - | ||||||
232 | } else { never executed: end of block | 0 | ||||||
233 | bytes -= d->sendingPackets.at(0); | - | ||||||
234 | d->sendingPackets.removeFirst(); | - | ||||||
235 | } executed 6289 times by 12 tests: end of block Executed by:
| 6289 | ||||||
236 | } | - | ||||||
237 | } executed 6267 times by 12 tests: end of block Executed by:
| 6267 | ||||||
238 | - | |||||||
239 | void QPacketProtocol::readyToRead() | - | ||||||
240 | { | - | ||||||
241 | Q_D(QPacketProtocol); | - | ||||||
242 | while (true) { | - | ||||||
243 | // Need to get trailing data | - | ||||||
244 | if (-1 == d->inProgressSize) {
| 8682-13118 | ||||||
245 | // We need a size header of sizeof(qint32) | - | ||||||
246 | if (static_cast<qint64>(sizeof(qint32)) > d->dev->bytesAvailable())
| 4436-8682 | ||||||
247 | return; executed 4436 times by 12 tests: return; Executed by:
| 4436 | ||||||
248 | - | |||||||
249 | // Read size header | - | ||||||
250 | qint32 inProgressSizeLE; | - | ||||||
251 | if (!d->readFromDevice((char *)&inProgressSizeLE, sizeof(qint32))) {
| 0-8682 | ||||||
252 | emit error(); | - | ||||||
253 | return; never executed: return; | 0 | ||||||
254 | } | - | ||||||
255 | d->inProgressSize = qFromLittleEndian(inProgressSizeLE); | - | ||||||
256 | - | |||||||
257 | // Check sizing constraints | - | ||||||
258 | if (d->inProgressSize < qint32(sizeof(qint32))) {
| 0-8682 | ||||||
259 | disconnect(d->dev, &QIODevice::readyRead, this, &QPacketProtocol::readyToRead); | - | ||||||
260 | disconnect(d->dev, &QIODevice::aboutToClose, this, &QPacketProtocol::aboutToClose); | - | ||||||
261 | disconnect(d->dev, &QIODevice::bytesWritten, this, &QPacketProtocol::bytesWritten); | - | ||||||
262 | d->dev = nullptr; | - | ||||||
263 | emit error(); | - | ||||||
264 | return; never executed: return; | 0 | ||||||
265 | } | - | ||||||
266 | - | |||||||
267 | d->inProgressSize -= sizeof(qint32); | - | ||||||
268 | } else { executed 8682 times by 12 tests: end of block Executed by:
| 8682 | ||||||
269 | - | |||||||
270 | const int bytesToRead = static_cast<int>( | - | ||||||
271 | qMin(d->dev->bytesAvailable(), | - | ||||||
272 | static_cast<qint64>(d->inProgressSize - d->inProgress.size()))); | - | ||||||
273 | - | |||||||
274 | QByteArray toRead(bytesToRead, Qt::Uninitialized); | - | ||||||
275 | if (!d->readFromDevice(toRead.data(), toRead.length())) {
| 0-8682 | ||||||
276 | emit error(); | - | ||||||
277 | return; never executed: return; | 0 | ||||||
278 | } | - | ||||||
279 | - | |||||||
280 | d->inProgress.append(toRead); | - | ||||||
281 | if (d->inProgressSize == d->inProgress.size()) {
| 0-8682 | ||||||
282 | // Packet has arrived! | - | ||||||
283 | d->packets.append(d->inProgress); | - | ||||||
284 | d->inProgressSize = -1; | - | ||||||
285 | d->inProgress.clear(); | - | ||||||
286 | - | |||||||
287 | d->waitingForPacket = false; | - | ||||||
288 | emit readyRead(); | - | ||||||
289 | } else executed 8682 times by 12 tests: end of block Executed by:
| 8682 | ||||||
290 | return; never executed: return; | 0 | ||||||
291 | } | - | ||||||
292 | } | - | ||||||
293 | } never executed: end of block | 0 | ||||||
294 | - | |||||||
295 | QPacketProtocolPrivate::QPacketProtocolPrivate(QIODevice *dev) : | - | ||||||
296 | inProgressSize(-1), waitingForPacket(false), dev(dev) | - | ||||||
297 | { | - | ||||||
298 | } executed 601 times by 12 tests: end of block Executed by:
| 601 | ||||||
299 | - | |||||||
300 | bool QPacketProtocolPrivate::writeToDevice(const char *bytes, qint64 size) | - | ||||||
301 | { | - | ||||||
302 | qint64 totalWritten = 0; | - | ||||||
303 | while (totalWritten < size) {
| 12578 | ||||||
304 | const qint64 chunkSize = dev->write(bytes + totalWritten, size - totalWritten); | - | ||||||
305 | if (chunkSize < 0)
| 0-12578 | ||||||
306 | return false; never executed: return false; | 0 | ||||||
307 | totalWritten += chunkSize; | - | ||||||
308 | } executed 12578 times by 12 tests: end of block Executed by:
| 12578 | ||||||
309 | return totalWritten == size; executed 12578 times by 12 tests: return totalWritten == size; Executed by:
| 12578 | ||||||
310 | } | - | ||||||
311 | - | |||||||
312 | bool QPacketProtocolPrivate::readFromDevice(char *buffer, qint64 size) | - | ||||||
313 | { | - | ||||||
314 | qint64 totalRead = 0; | - | ||||||
315 | while (totalRead < size) {
| 17364 | ||||||
316 | const qint64 chunkSize = dev->read(buffer + totalRead, size - totalRead); | - | ||||||
317 | if (chunkSize < 0)
| 0-17364 | ||||||
318 | return false; never executed: return false; | 0 | ||||||
319 | totalRead += chunkSize; | - | ||||||
320 | } executed 17364 times by 12 tests: end of block Executed by:
| 17364 | ||||||
321 | return totalRead == size; executed 17364 times by 12 tests: return totalRead == size; Executed by:
| 17364 | ||||||
322 | } | - | ||||||
323 | - | |||||||
324 | /*! | - | ||||||
325 | \fn void QPacketProtocol::readyRead() | - | ||||||
326 | - | |||||||
327 | Emitted whenever a new packet is received. Applications may use | - | ||||||
328 | QPacketProtocol::read() to retrieve this packet. | - | ||||||
329 | */ | - | ||||||
330 | - | |||||||
331 | /*! | - | ||||||
332 | \fn void QPacketProtocol::invalidPacket() | - | ||||||
333 | - | |||||||
334 | A packet larger than the maximum allowable packet size was received. The | - | ||||||
335 | packet will be discarded and, as it indicates corruption in the protocol, no | - | ||||||
336 | further packets will be received. | - | ||||||
337 | */ | - | ||||||
338 | - | |||||||
339 | QT_END_NAMESPACE | - | ||||||
Source code | Switch to Preprocessed file |