QFormDataPartBuilder: allow to override mime-type autodetection

If QT_CONFIG(mimetype) isn't set, this class was silently creating
invalid output. If no mimetype is specified by the user and the type
cannot be deduced then omit the content-type header.

Change-Id: Iff15462b94fa1e992369df26f74b2bd64d523f31
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Marc Mutz 2024-05-29 14:04:02 +02:00 committed by Mårten Nordheim
parent c4fc3a74b4
commit 5bf0c3d5d3
2 changed files with 63 additions and 18 deletions

View File

@ -3,6 +3,7 @@
#include "qformdatabuilder.h"
#include <QtCore/private/qstringconverter_p.h>
#if QT_CONFIG(mimetype)
#include "QtCore/qmimedatabase.h"
#endif
@ -86,10 +87,35 @@ static auto encodeFileName(QStringView view)
return R{encoding, needsUtf8 ? view.toUtf8() : view.toLatin1()};
}
QFormDataPartBuilder &QFormDataPartBuilder::setBodyHelper(const QByteArray &data,
QAnyStringView fileName)
static void convertInto_impl(QByteArray &dst, QUtf8StringView in)
{
m_originalBodyName = fileName.toString();
dst.clear();
dst += QByteArrayView{in}; // it's ASCII, anyway
}
static void convertInto_impl(QByteArray &dst, QLatin1StringView in)
{
dst.clear();
dst += QByteArrayView{in}; // it's ASCII, anyway
}
static void convertInto_impl(QByteArray &dst, QStringView in)
{
dst.resize(in.size());
(void)QLatin1::convertFromUnicode(dst.data(), in);
}
static void convertInto(QByteArray &dst, QAnyStringView in)
{
in.visit([&dst](auto in) { convertInto_impl(dst, in); });
}
QFormDataPartBuilder &QFormDataPartBuilder::setBodyHelper(const QByteArray &data,
QAnyStringView name,
QAnyStringView mimeType)
{
m_originalBodyName = name.toString();
convertInto(m_mimeType, mimeType);
m_body = data;
return *this;
}
@ -98,6 +124,9 @@ QFormDataPartBuilder &QFormDataPartBuilder::setBodyHelper(const QByteArray &data
Sets \a data as the body of this MIME part and, if given, \a fileName as the
file name parameter in the content disposition header.
If \a mimeType is not given (is empty), then QFormDataPartBuilder tries to
auto-detect the mime-type of \a data using QMimeDatabase.
A subsequent call to setBodyDevice() discards the body and the device will
be used instead.
@ -108,15 +137,19 @@ QFormDataPartBuilder &QFormDataPartBuilder::setBodyHelper(const QByteArray &data
*/
QFormDataPartBuilder &QFormDataPartBuilder::setBody(QByteArrayView data,
QAnyStringView fileName)
QAnyStringView fileName,
QAnyStringView mimeType)
{
return setBody(data.toByteArray(), fileName);
return setBody(data.toByteArray(), fileName, mimeType);
}
/*!
Sets \a body as the body device of this part and \a fileName as the file
name parameter in the content disposition header.
If \a mimeType is not given (is empty), then QFormDataPartBuilder tries to
auto-detect the mime-type of \a body using QMimeDatabase.
A subsequent call to setBody() discards the body device and the data set by
setBody() will be used instead.
@ -131,9 +164,11 @@ QFormDataPartBuilder &QFormDataPartBuilder::setBody(QByteArrayView data,
\sa setBody(), QHttpPart::setBodyDevice()
*/
QFormDataPartBuilder &QFormDataPartBuilder::setBodyDevice(QIODevice *body, QAnyStringView fileName)
QFormDataPartBuilder &QFormDataPartBuilder::setBodyDevice(QIODevice *body, QAnyStringView fileName,
QAnyStringView mimeType)
{
m_originalBodyName = fileName.toString();
convertInto(m_mimeType, mimeType);
m_body = body;
return *this;
}
@ -170,18 +205,23 @@ QHttpPart QFormDataPartBuilder::build()
}
#if QT_CONFIG(mimetype)
QMimeDatabase db;
QMimeType mimeType = std::visit([&](auto &arg) {
return db.mimeTypeForFileNameAndData(m_originalBodyName, arg);
}, m_body);
if (m_mimeType.isEmpty()) {
// auto-detect
QMimeDatabase db;
convertInto(m_mimeType, std::visit([&](auto &arg) {
return db.mimeTypeForFileNameAndData(m_originalBodyName, arg);
}, m_body).name());
}
#endif
for (qsizetype i = 0; i < m_httpHeaders.size(); i++) {
httpPart.setRawHeader(QByteArrayView(m_httpHeaders.nameAt(i)).toByteArray(),
m_httpHeaders.valueAt(i).toByteArray());
}
#if QT_CONFIG(mimetype)
httpPart.setHeader(QNetworkRequest::ContentTypeHeader, mimeType.name());
#endif
if (!m_mimeType.isEmpty())
httpPart.setHeader(QNetworkRequest::ContentTypeHeader, m_mimeType);
httpPart.setHeader(QNetworkRequest::ContentDispositionHeader, m_headerValue);
if (auto d = std::get_if<QIODevice*>(&m_body))

View File

@ -57,22 +57,27 @@ public:
Q_NETWORK_EXPORT ~QFormDataPartBuilder();
Q_WEAK_OVERLOAD QFormDataPartBuilder &setBody(const QByteArray &data,
QAnyStringView fileName = {})
{ return setBodyHelper(data, fileName); }
QAnyStringView fileName = {},
QAnyStringView mimeType = {})
{ return setBodyHelper(data, fileName, mimeType); }
Q_NETWORK_EXPORT QFormDataPartBuilder &setBody(QByteArrayView data,
QAnyStringView fileName = {});
QAnyStringView fileName = {},
QAnyStringView mimeType = {});
Q_NETWORK_EXPORT QFormDataPartBuilder &setBodyDevice(QIODevice *body,
QAnyStringView fileName = {});
QAnyStringView fileName = {},
QAnyStringView mimeType = {});
Q_NETWORK_EXPORT QFormDataPartBuilder &setHeaders(const QHttpHeaders &headers);
private:
Q_DISABLE_COPY(QFormDataPartBuilder)
Q_NETWORK_EXPORT QFormDataPartBuilder &setBodyHelper(const QByteArray &data,
QAnyStringView fileName = {});
QAnyStringView fileName,
QAnyStringView mimeType);
Q_NETWORK_EXPORT QHttpPart build();
QByteArray m_headerValue;
QByteArray m_mimeType;
QString m_originalBodyName;
QHttpHeaders m_httpHeaders;
std::variant<QIODevice*, QByteArray> m_body;