1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qhttpmultipart.h"
5#include "qhttpmultipart_p.h"
6#include "QtCore/qdatetime.h" // for initializing the random number generator with QTime
7#include "QtCore/qmutex.h"
8#include "QtCore/qrandom.h"
9
10QT_BEGIN_NAMESPACE
11
12/*!
13 \class QHttpPart
14 \brief The QHttpPart class holds a body part to be used inside a
15 HTTP multipart MIME message.
16 \since 4.8
17
18 \ingroup network
19 \ingroup shared
20 \inmodule QtNetwork
21
22 The QHttpPart class holds a body part to be used inside a HTTP
23 multipart MIME message (which is represented by the QHttpMultiPart class).
24 A QHttpPart consists of a header block
25 and a data block, which are separated by each other by two
26 consecutive new lines. An example for one part would be:
27
28 \snippet code/src_network_access_qhttppart.cpp 0
29
30 For setting headers, use setHeader() and setRawHeader(), which behave
31 exactly like QNetworkRequest::setHeader() and QNetworkRequest::setRawHeader().
32
33 For reading small pieces of data, use setBody(); for larger data blocks
34 like e.g. images, use setBodyDevice(). The latter method saves memory by
35 not copying the data internally, but reading directly from the device.
36 This means that the device must be opened and readable at the moment when
37 the multipart message containing the body part is sent on the network via
38 QNetworkAccessManager::post().
39
40 To construct a QHttpPart with a small body, consider the following snippet
41 (this produces the data shown in the example above):
42
43 \snippet code/src_network_access_qhttppart.cpp 1
44
45 To construct a QHttpPart reading from a device (e.g. a file), the following
46 can be applied:
47
48 \snippet code/src_network_access_qhttppart.cpp 2
49
50 Be aware that QHttpPart does not take ownership of the device when set, so
51 it is the developer's responsibility to destroy it when it is not needed anymore.
52 A good idea might be to set the multipart message as parent object for the device,
53 as documented at the documentation for QHttpMultiPart.
54
55 \sa QHttpMultiPart, QNetworkAccessManager
56*/
57
58
59/*!
60 Constructs an empty QHttpPart object.
61*/
62QHttpPart::QHttpPart() : d(new QHttpPartPrivate)
63{
64}
65
66/*!
67 Creates a copy of \a other.
68*/
69QHttpPart::QHttpPart(const QHttpPart &other) : d(other.d)
70{
71}
72
73/*!
74 Destroys this QHttpPart.
75*/
76QHttpPart::~QHttpPart()
77{
78 d = nullptr;
79}
80
81/*!
82 Creates a copy of \a other.
83*/
84QHttpPart &QHttpPart::operator=(const QHttpPart &other)
85{
86 d = other.d;
87 return *this;
88}
89
90/*!
91 \fn void QHttpPart::swap(QHttpPart &other)
92 \since 5.0
93 \memberswap{HTTP part}
94*/
95
96/*!
97 Returns \c true if this object is the same as \a other (i.e., if they
98 have the same headers and body).
99
100 \sa operator!=()
101*/
102bool QHttpPart::operator==(const QHttpPart &other) const
103{
104 return d == other.d || *d == *other.d;
105}
106
107/*!
108 \fn bool QHttpPart::operator!=(const QHttpPart &other) const
109
110 Returns \c true if this object is not the same as \a other.
111
112 \sa operator==()
113*/
114
115/*!
116 Sets the value of the known header \a header to be \a value,
117 overriding any previously set headers.
118
119 \sa QNetworkRequest::KnownHeaders, setRawHeader(), QNetworkRequest::setHeader()
120*/
121void QHttpPart::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
122{
123 d->setCookedHeader(header, value);
124}
125
126/*!
127 Sets the header \a headerName to be of value \a headerValue. If \a
128 headerName corresponds to a known header (see
129 QNetworkRequest::KnownHeaders), the raw format will be parsed and
130 the corresponding "cooked" header will be set as well.
131
132 \note Setting the same header twice overrides the previous
133 setting. To accomplish the behaviour of multiple HTTP headers of
134 the same name, you should concatenate the two values, separating
135 them with a comma (",") and set one single raw header.
136
137 \sa QNetworkRequest::KnownHeaders, setHeader(), QNetworkRequest::setRawHeader()
138*/
139void QHttpPart::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
140{
141 d->setRawHeader(key: headerName, value: headerValue);
142}
143
144/*!
145 Sets the body of this MIME part to \a body. The body set with this method
146 will be used unless the device is set via setBodyDevice(). For a large
147 amount of data (e.g. an image), use setBodyDevice(), which will not copy
148 the data internally.
149
150 \sa setBodyDevice()
151*/
152void QHttpPart::setBody(const QByteArray &body)
153{
154 d->setBody(body);
155}
156
157/*!
158 Sets the device to read the content from to \a device. For large amounts of data
159 this method should be preferred over setBody(),
160 because the content is not copied when using this method, but read
161 directly from the device.
162 \a device must be open and readable. QHttpPart does not take ownership
163 of \a device, i.e. the device must be closed and destroyed if necessary.
164 if \a device is sequential (e.g. sockets, but not files),
165 QNetworkAccessManager::post() should be called after \a device has
166 emitted finished().
167 For unsetting the device and using data set via setBody(), use
168 "setBodyDevice(0)".
169
170 \sa setBody(), QNetworkAccessManager::post()
171 */
172void QHttpPart::setBodyDevice(QIODevice *device)
173{
174 d->setBodyDevice(device);
175}
176
177
178
179/*!
180 \class QHttpMultiPart
181 \brief The QHttpMultiPart class resembles a MIME multipart message to be sent over HTTP.
182 \since 4.8
183
184 \ingroup network
185 \inmodule QtNetwork
186
187 The QHttpMultiPart resembles a MIME multipart message, as described in RFC 2046,
188 which is to be sent over HTTP.
189 A multipart message consists of an arbitrary number of body parts (see QHttpPart),
190 which are separated by a unique boundary. The boundary of the QHttpMultiPart is
191 constructed with the string "boundary_.oOo._" followed by random characters,
192 and provides enough uniqueness to make sure it does not occur inside the parts itself.
193 If desired, the boundary can still be set via setBoundary().
194
195 As an example, consider the following code snippet, which constructs a multipart
196 message containing a text part followed by an image part:
197
198 \snippet code/src_network_access_qhttpmultipart.cpp 0
199
200 \sa QHttpPart, QNetworkAccessManager::post()
201*/
202
203/*!
204 \enum QHttpMultiPart::ContentType
205
206 List of known content types for a multipart subtype as described
207 in RFC 2046 and others.
208
209 \value MixedType corresponds to the "multipart/mixed" subtype,
210 meaning the body parts are independent of each other, as described
211 in RFC 2046.
212
213 \value RelatedType corresponds to the "multipart/related" subtype,
214 meaning the body parts are related to each other, as described in RFC 2387.
215
216 \value FormDataType corresponds to the "multipart/form-data"
217 subtype, meaning the body parts contain form elements, as described in RFC 2388.
218
219 \value AlternativeType corresponds to the "multipart/alternative"
220 subtype, meaning the body parts are alternative representations of
221 the same information, as described in RFC 2046.
222
223 \sa setContentType()
224*/
225
226/*!
227 Constructs a QHttpMultiPart with content type MixedType and sets
228 \a parent as the parent object.
229
230 \sa QHttpMultiPart::ContentType
231*/
232QHttpMultiPart::QHttpMultiPart(QObject *parent) : QObject(*new QHttpMultiPartPrivate, parent)
233{
234 Q_D(QHttpMultiPart);
235 d->contentType = MixedType;
236}
237
238/*!
239 Constructs a QHttpMultiPart with content type \a contentType and
240 sets parent as the parent object.
241
242 \sa QHttpMultiPart::ContentType
243*/
244QHttpMultiPart::QHttpMultiPart(QHttpMultiPart::ContentType contentType, QObject *parent) : QObject(*new QHttpMultiPartPrivate, parent)
245{
246 Q_D(QHttpMultiPart);
247 d->contentType = contentType;
248}
249
250/*!
251 Destroys the multipart.
252*/
253QHttpMultiPart::~QHttpMultiPart()
254{
255}
256
257/*!
258 Appends \a httpPart to this multipart.
259*/
260void QHttpMultiPart::append(const QHttpPart &httpPart)
261{
262 d_func()->parts.append(t: httpPart);
263}
264
265/*!
266 Sets the content type to \a contentType. The content type will be used
267 in the HTTP header section when sending the multipart message via
268 QNetworkAccessManager::post().
269 In case you want to use a multipart subtype not contained in
270 QHttpMultiPart::ContentType,
271 you can add the "Content-Type" header field to the QNetworkRequest
272 by hand, and then use this request together with the multipart
273 message for posting.
274
275 \sa QHttpMultiPart::ContentType, QNetworkAccessManager::post()
276*/
277void QHttpMultiPart::setContentType(QHttpMultiPart::ContentType contentType)
278{
279 d_func()->contentType = contentType;
280}
281
282/*!
283 returns the boundary.
284
285 \sa setBoundary()
286*/
287QByteArray QHttpMultiPart::boundary() const
288{
289 return d_func()->boundary;
290}
291
292/*!
293 Sets the boundary to \a boundary.
294
295 Usually, you do not need to generate a boundary yourself; upon construction
296 the boundary is initiated with the string "boundary_.oOo._" followed by random
297 characters, and provides enough uniqueness to make sure it does not occur
298 inside the parts itself.
299
300 \sa boundary()
301*/
302void QHttpMultiPart::setBoundary(const QByteArray &boundary)
303{
304 d_func()->boundary = boundary;
305}
306
307
308
309// ------------------------------------------------------------------
310// ----------- implementations of private classes: ------------------
311// ------------------------------------------------------------------
312
313
314
315qint64 QHttpPartPrivate::bytesAvailable() const
316{
317 checkHeaderCreated();
318 qint64 bytesAvailable = header.size();
319 if (bodyDevice) {
320 bytesAvailable += bodyDevice->bytesAvailable() - readPointer;
321 } else {
322 bytesAvailable += body.size() - readPointer;
323 }
324 // the device might have closed etc., so make sure we do not return a negative value
325 return qMax(a: bytesAvailable, b: (qint64) 0);
326}
327
328qint64 QHttpPartPrivate::readData(char *data, qint64 maxSize)
329{
330 checkHeaderCreated();
331 qint64 bytesRead = 0;
332 qint64 headerDataCount = header.size();
333
334 // read header if it has not been read yet
335 if (readPointer < headerDataCount) {
336 bytesRead = qMin(a: headerDataCount - readPointer, b: maxSize);
337 const char *headerData = header.constData();
338 memcpy(dest: data, src: headerData + readPointer, n: bytesRead);
339 readPointer += bytesRead;
340 }
341 // read content if there is still space
342 if (bytesRead < maxSize) {
343 if (bodyDevice) {
344 qint64 dataBytesRead = bodyDevice->read(data: data + bytesRead, maxlen: maxSize - bytesRead);
345 if (dataBytesRead == -1)
346 return -1;
347 bytesRead += dataBytesRead;
348 readPointer += dataBytesRead;
349 } else {
350 qint64 contentBytesRead = qMin(a: body.size() - readPointer + headerDataCount, b: maxSize - bytesRead);
351 const char *contentData = body.constData();
352 // if this method is called several times, we need to find the
353 // right offset in the content ourselves:
354 memcpy(dest: data + bytesRead, src: contentData + readPointer - headerDataCount, n: contentBytesRead);
355 bytesRead += contentBytesRead;
356 readPointer += contentBytesRead;
357 }
358 }
359 return bytesRead;
360}
361
362qint64 QHttpPartPrivate::size() const
363{
364 checkHeaderCreated();
365 qint64 size = header.size();
366 if (bodyDevice) {
367 size += bodyDevice->size();
368 } else {
369 size += body.size();
370 }
371 return size;
372}
373
374bool QHttpPartPrivate::reset()
375{
376 bool ret = true;
377 if (bodyDevice)
378 if (!bodyDevice->reset())
379 ret = false;
380 readPointer = 0;
381 return ret;
382}
383void QHttpPartPrivate::checkHeaderCreated() const
384{
385 if (!headerCreated) {
386 // copied from QHttpNetworkRequestPrivate::header() and adapted
387 const auto h = headers();
388 for (qsizetype i = 0; i < h.size(); ++i) {
389 const auto name = h.nameAt(i);
390 header += QByteArrayView(name.data(), name.size()) + ": " + h.valueAt(i) + "\r\n";
391 }
392
393 header += "\r\n";
394 headerCreated = true;
395 }
396}
397
398QHttpMultiPartPrivate::QHttpMultiPartPrivate() : contentType(QHttpMultiPart::MixedType), device(new QHttpMultiPartIODevice(this))
399{
400 // 24 random bytes, becomes 32 characters when encoded to Base64
401 quint32 random[6];
402 QRandomGenerator::global()->fillRange(buffer&: random);
403 boundary = "boundary_.oOo._"
404 + QByteArray::fromRawData(data: reinterpret_cast<char *>(random), size: sizeof(random)).toBase64();
405
406 // boundary must not be longer than 70 characters, see RFC 2046, section 5.1.1
407 Q_ASSERT(boundary.size() <= 70);
408}
409
410QHttpMultiPartPrivate::~QHttpMultiPartPrivate()
411{
412 delete device;
413}
414
415QHttpMultiPartIODevice::~QHttpMultiPartIODevice()
416 = default;
417
418qint64 QHttpMultiPartIODevice::size() const
419{
420 // if not done yet, we calculate the size and the offsets of each part,
421 // including boundary (needed later in readData)
422 if (deviceSize == -1) {
423 qint64 currentSize = 0;
424 qint64 boundaryCount = multiPart->boundary.size();
425 for (int a = 0; a < multiPart->parts.size(); a++) {
426 partOffsets.append(t: currentSize);
427 // 4 additional bytes for the "--" before and the "\r\n" after the boundary,
428 // and 2 bytes for the "\r\n" after the content
429 currentSize += boundaryCount + 4 + multiPart->parts.at(i: a).d->size() + 2;
430 }
431 currentSize += boundaryCount + 6; // size for ending boundary, 2 beginning and ending dashes and "\r\n"
432 deviceSize = currentSize;
433 }
434 return deviceSize;
435}
436
437bool QHttpMultiPartIODevice::isSequential() const
438{
439 for (int a = 0; a < multiPart->parts.size(); a++) {
440 QIODevice *device = multiPart->parts.at(i: a).d->bodyDevice;
441 // we are sequential if any of the bodyDevices of our parts are sequential;
442 // when reading from a byte array, we are not sequential
443 if (device && device->isSequential())
444 return true;
445 }
446 return false;
447}
448
449bool QHttpMultiPartIODevice::reset()
450{
451 // Reset QIODevice's data
452 QIODevice::reset();
453 for (int a = 0; a < multiPart->parts.size(); a++)
454 if (!multiPart->parts[a].d->reset())
455 return false;
456 readPointer = 0;
457 return true;
458}
459qint64 QHttpMultiPartIODevice::readData(char *data, qint64 maxSize)
460{
461 qint64 bytesRead = 0, index = 0;
462
463 // skip the parts we have already read
464 while (index < multiPart->parts.size() &&
465 readPointer >= partOffsets.at(i: index) + multiPart->parts.at(i: index).d->size()
466 + multiPart->boundary.size() + 6) // 6 == 2 boundary dashes, \r\n after boundary, \r\n after multipart
467 index++;
468
469 // read the data
470 while (bytesRead < maxSize && index < multiPart->parts.size()) {
471
472 // check whether we need to read the boundary of the current part
473 QByteArray boundaryData = "--" + multiPart->boundary + "\r\n";
474 qint64 boundaryCount = boundaryData.size();
475 qint64 partIndex = readPointer - partOffsets.at(i: index);
476 if (partIndex < boundaryCount) {
477 qint64 boundaryBytesRead = qMin(a: boundaryCount - partIndex, b: maxSize - bytesRead);
478 memcpy(dest: data + bytesRead, src: boundaryData.constData() + partIndex, n: boundaryBytesRead);
479 bytesRead += boundaryBytesRead;
480 readPointer += boundaryBytesRead;
481 partIndex += boundaryBytesRead;
482 }
483
484 // check whether we need to read the data of the current part
485 if (bytesRead < maxSize && partIndex >= boundaryCount && partIndex < boundaryCount + multiPart->parts.at(i: index).d->size()) {
486 qint64 dataBytesRead = multiPart->parts[index].d->readData(data: data + bytesRead, maxSize: maxSize - bytesRead);
487 if (dataBytesRead == -1)
488 return -1;
489 bytesRead += dataBytesRead;
490 readPointer += dataBytesRead;
491 partIndex += dataBytesRead;
492 }
493
494 // check whether we need to read the ending CRLF of the current part
495 if (bytesRead < maxSize && partIndex >= boundaryCount + multiPart->parts.at(i: index).d->size()) {
496 if (bytesRead == maxSize - 1)
497 return bytesRead;
498 memcpy(dest: data + bytesRead, src: "\r\n", n: 2);
499 bytesRead += 2;
500 readPointer += 2;
501 index++;
502 }
503 }
504 // check whether we need to return the final boundary
505 if (bytesRead < maxSize && index == multiPart->parts.size()) {
506 QByteArray finalBoundary = "--" + multiPart->boundary + "--\r\n";
507 qint64 boundaryIndex = readPointer + finalBoundary.size() - size();
508 qint64 lastBoundaryBytesRead = qMin(a: finalBoundary.size() - boundaryIndex, b: maxSize - bytesRead);
509 memcpy(dest: data + bytesRead, src: finalBoundary.constData() + boundaryIndex, n: lastBoundaryBytesRead);
510 bytesRead += lastBoundaryBytesRead;
511 readPointer += lastBoundaryBytesRead;
512 }
513 return bytesRead;
514}
515
516qint64 QHttpMultiPartIODevice::writeData(const char *data, qint64 maxSize)
517{
518 Q_UNUSED(data);
519 Q_UNUSED(maxSize);
520 return -1;
521}
522
523#ifndef QT_NO_DEBUG_STREAM
524
525/*!
526 \fn QDebug QHttpPart::operator<<(QDebug debug, const QHttpPart &part)
527
528 Writes the \a part into the \a debug object for debugging purposes.
529 Unless a device is set, the size of the body is shown.
530
531 \sa {Debugging Techniques}
532 \since 6.8
533*/
534
535QDebug operator<<(QDebug debug, const QHttpPart &part)
536{
537 const QDebugStateSaver saver(debug);
538 debug.resetFormat().nospace().noquote();
539
540 debug << "QHttpPart(headers = ["
541 << part.d->cookedHeaders
542 << "], http headers = ["
543 << part.d->httpHeaders
544 << "],";
545
546 if (part.d->bodyDevice) {
547 debug << " bodydevice = ["
548 << part.d->bodyDevice
549 << ", is open: "
550 << part.d->bodyDevice->isOpen()
551 << "]";
552 } else {
553 debug << " size of body = "
554 << part.d->body.size()
555 << " bytes";
556 }
557
558 debug << ")";
559
560 return debug;
561}
562
563#endif // QT_NO_DEBUG_STREAM
564
565
566QT_END_NAMESPACE
567
568#include "moc_qhttpmultipart.cpp"
569

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/network/access/qhttpmultipart.cpp