QFormDataPartBuilder: delay encoding of the part's name to build() time
This is a prerequisite for making QFormDataBuilder::part() return an existing part instead of creating new parts with the same name (idempotence), which was pointed out in API review. Task-number: QTBUG-125985 Change-Id: I642e56cc7cb3562304573930a01aa1aafa817f36 Reviewed-by: Mate Barany <mate.barany@qt.io> (cherry picked from commit 9503e2c92c2d3b8f5bb9c86274d2430eb0a57880) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
aefa427459
commit
70f38f48c1
@ -30,24 +30,6 @@ QT_BEGIN_NAMESPACE
|
|||||||
\sa QHttpPart, QHttpMultiPart, QFormDataBuilder
|
\sa QHttpPart, QHttpMultiPart, QFormDataBuilder
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static QByteArray nameToByteArray(QStringView view)
|
|
||||||
{
|
|
||||||
return view.toUtf8();
|
|
||||||
}
|
|
||||||
|
|
||||||
static QByteArray nameToByteArray(QLatin1StringView view)
|
|
||||||
{
|
|
||||||
if (!QtPrivate::isAscii(view))
|
|
||||||
return view.toString().toUtf8(); // ### optimize
|
|
||||||
|
|
||||||
return QByteArray::fromRawData(view.data(), view.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
static QByteArray nameToByteArray(QUtf8StringView view)
|
|
||||||
{
|
|
||||||
return QByteArray::fromRawData(view.data(), view.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void escapeNameAndAppend(QByteArray &dst, QByteArrayView src)
|
static void escapeNameAndAppend(QByteArray &dst, QByteArrayView src)
|
||||||
{
|
{
|
||||||
for (auto c : src) {
|
for (auto c : src) {
|
||||||
@ -57,20 +39,32 @@ static void escapeNameAndAppend(QByteArray &dst, QByteArrayView src)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void escapeNameAndAppend(QByteArray &dst, QStringView src)
|
||||||
|
{
|
||||||
|
qsizetype last = 0;
|
||||||
|
|
||||||
|
// equivalent to for (auto chunk : qTokenize(src, any_of("\\\""))), if there was such a thing
|
||||||
|
for (qsizetype i = 0, end = src.size(); i != end; ++i) {
|
||||||
|
const auto c = src[i];
|
||||||
|
if (c == u'"' || c == u'\\') {
|
||||||
|
const auto chunk = src.sliced(last, i - last);
|
||||||
|
dst += QUtf8::convertFromUnicode(chunk); // ### optimize
|
||||||
|
dst += '\\';
|
||||||
|
last = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dst += QUtf8::convertFromUnicode(src.sliced(last));
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Constructs a QFormDataPartBuilder object and sets \a name as the name
|
Constructs a QFormDataPartBuilder object and sets \a name as the name
|
||||||
parameter of the form-data.
|
parameter of the form-data.
|
||||||
*/
|
*/
|
||||||
QFormDataPartBuilder::QFormDataPartBuilder(QAnyStringView name, PrivateConstructor /*unused*/)
|
QFormDataPartBuilder::QFormDataPartBuilder(QAnyStringView name, PrivateConstructor /*unused*/)
|
||||||
|
: m_name{name.toString()}
|
||||||
{
|
{
|
||||||
static_assert(std::is_nothrow_move_constructible_v<decltype(m_body)>);
|
static_assert(std::is_nothrow_move_constructible_v<decltype(m_body)>);
|
||||||
static_assert(std::is_nothrow_move_assignable_v<decltype(m_body)>);
|
static_assert(std::is_nothrow_move_assignable_v<decltype(m_body)>);
|
||||||
|
|
||||||
const auto enc = name.visit([](auto name) { return nameToByteArray(name); });
|
|
||||||
|
|
||||||
m_headerValue += "form-data; name=\"";
|
|
||||||
escapeNameAndAppend(m_headerValue, enc);
|
|
||||||
m_headerValue += "\"";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -210,19 +204,25 @@ QHttpPart QFormDataPartBuilder::build()
|
|||||||
{
|
{
|
||||||
QHttpPart httpPart;
|
QHttpPart httpPart;
|
||||||
|
|
||||||
|
QByteArray headerValue;
|
||||||
|
|
||||||
|
headerValue += "form-data; name=\"";
|
||||||
|
escapeNameAndAppend(headerValue, m_name);
|
||||||
|
headerValue += "\"";
|
||||||
|
|
||||||
if (!m_originalBodyName.isNull()) {
|
if (!m_originalBodyName.isNull()) {
|
||||||
const bool utf8 = !QtPrivate::isAscii(m_originalBodyName);
|
const bool utf8 = !QtPrivate::isAscii(m_originalBodyName);
|
||||||
const auto enc = utf8 ? m_originalBodyName.toUtf8() : m_originalBodyName.toLatin1();
|
const auto enc = utf8 ? m_originalBodyName.toUtf8() : m_originalBodyName.toLatin1();
|
||||||
m_headerValue += "; filename=\"";
|
headerValue += "; filename=\"";
|
||||||
escapeNameAndAppend(m_headerValue, enc);
|
escapeNameAndAppend(headerValue, enc);
|
||||||
m_headerValue += "\"";
|
headerValue += "\"";
|
||||||
if (utf8) {
|
if (utf8) {
|
||||||
// For 'filename*' production see
|
// For 'filename*' production see
|
||||||
// https://datatracker.ietf.org/doc/html/rfc5987#section-3.2.1
|
// https://datatracker.ietf.org/doc/html/rfc5987#section-3.2.1
|
||||||
// For providing both filename and filename* parameters see
|
// For providing both filename and filename* parameters see
|
||||||
// https://datatracker.ietf.org/doc/html/rfc6266#section-4.3 and
|
// https://datatracker.ietf.org/doc/html/rfc6266#section-4.3 and
|
||||||
// https://datatracker.ietf.org/doc/html/rfc8187#section-4.2
|
// https://datatracker.ietf.org/doc/html/rfc8187#section-4.2
|
||||||
m_headerValue += "; filename*=UTF-8''" + enc.toPercentEncoding();
|
headerValue += "; filename*=UTF-8''" + enc.toPercentEncoding();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,7 +244,7 @@ QHttpPart QFormDataPartBuilder::build()
|
|||||||
if (!m_mimeType.isEmpty())
|
if (!m_mimeType.isEmpty())
|
||||||
httpPart.setHeader(QNetworkRequest::ContentTypeHeader, m_mimeType);
|
httpPart.setHeader(QNetworkRequest::ContentTypeHeader, m_mimeType);
|
||||||
|
|
||||||
httpPart.setHeader(QNetworkRequest::ContentDispositionHeader, m_headerValue);
|
httpPart.setHeader(QNetworkRequest::ContentDispositionHeader, std::move(headerValue));
|
||||||
|
|
||||||
if (auto d = std::get_if<QIODevice*>(&m_body))
|
if (auto d = std::get_if<QIODevice*>(&m_body))
|
||||||
httpPart.setBodyDevice(*d);
|
httpPart.setBodyDevice(*d);
|
||||||
|
@ -34,7 +34,7 @@ public:
|
|||||||
Q_NETWORK_EXPORT explicit QFormDataPartBuilder(QAnyStringView name, PrivateConstructor);
|
Q_NETWORK_EXPORT explicit QFormDataPartBuilder(QAnyStringView name, PrivateConstructor);
|
||||||
|
|
||||||
QFormDataPartBuilder(QFormDataPartBuilder &&other) noexcept
|
QFormDataPartBuilder(QFormDataPartBuilder &&other) noexcept
|
||||||
: m_headerValue(std::move(other.m_headerValue)),
|
: m_name(std::move(other.m_name)),
|
||||||
m_originalBodyName(std::move(other.m_originalBodyName)),
|
m_originalBodyName(std::move(other.m_originalBodyName)),
|
||||||
m_httpHeaders(std::move(other.m_httpHeaders)),
|
m_httpHeaders(std::move(other.m_httpHeaders)),
|
||||||
m_body(std::move(other.m_body)),
|
m_body(std::move(other.m_body)),
|
||||||
@ -46,7 +46,7 @@ public:
|
|||||||
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QFormDataPartBuilder)
|
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QFormDataPartBuilder)
|
||||||
void swap(QFormDataPartBuilder &other) noexcept
|
void swap(QFormDataPartBuilder &other) noexcept
|
||||||
{
|
{
|
||||||
m_headerValue.swap(other.m_headerValue);
|
m_name.swap(other.m_name);
|
||||||
m_originalBodyName.swap(other.m_originalBodyName);
|
m_originalBodyName.swap(other.m_originalBodyName);
|
||||||
m_httpHeaders.swap(other.m_httpHeaders);
|
m_httpHeaders.swap(other.m_httpHeaders);
|
||||||
m_body.swap(other.m_body);
|
m_body.swap(other.m_body);
|
||||||
@ -75,7 +75,7 @@ private:
|
|||||||
QAnyStringView mimeType);
|
QAnyStringView mimeType);
|
||||||
QHttpPart build();
|
QHttpPart build();
|
||||||
|
|
||||||
QByteArray m_headerValue;
|
QString m_name;
|
||||||
QByteArray m_mimeType;
|
QByteArray m_mimeType;
|
||||||
QString m_originalBodyName;
|
QString m_originalBodyName;
|
||||||
QHttpHeaders m_httpHeaders;
|
QHttpHeaders m_httpHeaders;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user