summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Mutz <[email protected]>2024-06-19 10:16:02 +0200
committerMarc Mutz <[email protected]>2024-06-27 17:23:24 +0200
commit9503e2c92c2d3b8f5bb9c86274d2430eb0a57880 (patch)
tree745ec53d843fd205ab9b4e43b374d184acee38f3
parent663d992672852741e10d83e6c5184f348ada78bf (diff)
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. Pick-to: 6.8 Task-number: QTBUG-125985 Change-Id: I642e56cc7cb3562304573930a01aa1aafa817f36 Reviewed-by: Mate Barany <[email protected]>
-rw-r--r--src/network/access/qformdatabuilder.cpp58
-rw-r--r--src/network/access/qformdatabuilder.h6
2 files changed, 32 insertions, 32 deletions
diff --git a/src/network/access/qformdatabuilder.cpp b/src/network/access/qformdatabuilder.cpp
index 0428f09a813..72ef96c253a 100644
--- a/src/network/access/qformdatabuilder.cpp
+++ b/src/network/access/qformdatabuilder.cpp
@@ -30,24 +30,6 @@ QT_BEGIN_NAMESPACE
\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)
{
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
parameter of the form-data.
*/
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_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;
+ QByteArray headerValue;
+
+ headerValue += "form-data; name=\"";
+ escapeNameAndAppend(headerValue, m_name);
+ headerValue += "\"";
+
if (!m_originalBodyName.isNull()) {
const bool utf8 = !QtPrivate::isAscii(m_originalBodyName);
const auto enc = utf8 ? m_originalBodyName.toUtf8() : m_originalBodyName.toLatin1();
- m_headerValue += "; filename=\"";
- escapeNameAndAppend(m_headerValue, enc);
- m_headerValue += "\"";
+ headerValue += "; filename=\"";
+ escapeNameAndAppend(headerValue, enc);
+ headerValue += "\"";
if (utf8) {
// For 'filename*' production see
// https://datatracker.ietf.org/doc/html/rfc5987#section-3.2.1
// 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/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())
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))
httpPart.setBodyDevice(*d);
diff --git a/src/network/access/qformdatabuilder.h b/src/network/access/qformdatabuilder.h
index 4305ff8cb76..ddbb8ee0fa2 100644
--- a/src/network/access/qformdatabuilder.h
+++ b/src/network/access/qformdatabuilder.h
@@ -34,7 +34,7 @@ public:
Q_NETWORK_EXPORT explicit QFormDataPartBuilder(QAnyStringView name, PrivateConstructor);
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_httpHeaders(std::move(other.m_httpHeaders)),
m_body(std::move(other.m_body)),
@@ -46,7 +46,7 @@ public:
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QFormDataPartBuilder)
void swap(QFormDataPartBuilder &other) noexcept
{
- m_headerValue.swap(other.m_headerValue);
+ m_name.swap(other.m_name);
m_originalBodyName.swap(other.m_originalBodyName);
m_httpHeaders.swap(other.m_httpHeaders);
m_body.swap(other.m_body);
@@ -75,7 +75,7 @@ private:
QAnyStringView mimeType);
QHttpPart build();
- QByteArray m_headerValue;
+ QString m_name;
QByteArray m_mimeType;
QString m_originalBodyName;
QHttpHeaders m_httpHeaders;