Add a function in NetworkCommon parseHeaderList which parses a header list as a string into a vector of header pairs (#2623)

This commit is contained in:
pajlada 2021-04-17 13:49:19 +02:00 committed by GitHub
parent cceadf473a
commit 2f906c5504
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 119 additions and 11 deletions

View file

@ -134,6 +134,7 @@ SOURCES += \
src/common/Env.cpp \
src/common/LinkParser.cpp \
src/common/Modes.cpp \
src/common/NetworkCommon.cpp \
src/common/NetworkManager.cpp \
src/common/NetworkPrivate.cpp \
src/common/NetworkRequest.cpp \

View file

@ -30,6 +30,8 @@ set(SOURCE_FILES main.cpp
common/LinkParser.hpp
common/Modes.cpp
common/Modes.hpp
common/NetworkCommon.cpp
common/NetworkCommon.hpp
common/NetworkManager.cpp
common/NetworkManager.hpp
common/NetworkPrivate.cpp

View file

@ -0,0 +1,35 @@
#include "common/NetworkCommon.hpp"
#include <QStringList>
namespace chatterino {
std::vector<std::pair<QByteArray, QByteArray>> parseHeaderList(
const QString &headerListString)
{
std::vector<std::pair<QByteArray, QByteArray>> res;
// Split the string into a list of header pairs
// e.g. "Authorization:secretkey;NextHeader:boo" turning into ["Authorization:secretkey","NextHeader:boo"]
auto headerPairs = headerListString.split(";");
for (const auto &headerPair : headerPairs)
{
const auto headerName =
headerPair.section(":", 0, 0).trimmed().toUtf8();
const auto headerValue = headerPair.section(":", 1).trimmed().toUtf8();
if (headerName.isEmpty() || headerValue.isEmpty())
{
// The header part either didn't contain a : or the name/value was empty
// Skip the value
continue;
}
res.emplace_back(headerName, headerValue);
}
return res;
}
} // namespace chatterino

View file

@ -1,6 +1,9 @@
#pragma once
#include <functional>
#include <vector>
#include <QString>
class QNetworkReply;
@ -22,4 +25,13 @@ enum class NetworkRequestType {
Patch,
};
// parseHeaderList takes a list of headers in string form,
// where each header pair is separated by semicolons (;) and the header name and value is divided by a colon (:)
//
// We return a vector of pairs, where the first value is the header name and the second value is the header value
//
// e.g. "Authorization:secretkey;NextHeader:boo" will return [{"Authorization", "secretkey"}, {"NextHeader", "boo"}]
std::vector<std::pair<QByteArray, QByteArray>> parseHeaderList(
const QString &headerListString);
} // namespace chatterino

View file

@ -106,16 +106,12 @@ NetworkRequest NetworkRequest::header(const char *headerName,
return std::move(*this);
}
NetworkRequest NetworkRequest::headerList(const QStringList &headers) &&
NetworkRequest NetworkRequest::headerList(
const std::vector<std::pair<QByteArray, QByteArray>> &headers) &&
{
for (const QString &header : headers)
for (const auto &[headerName, headerValue] : headers)
{
const QStringList thisHeader = header.trimmed().split(":");
if (thisHeader.size() == 2)
{
this->data->request_.setRawHeader(thisHeader[0].trimmed().toUtf8(),
thisHeader[1].trimmed().toUtf8());
}
this->data->request_.setRawHeader(headerName, headerValue);
}
return std::move(*this);
}

View file

@ -54,7 +54,8 @@ public:
NetworkRequest header(const char *headerName, const char *value) &&;
NetworkRequest header(const char *headerName, const QByteArray &value) &&;
NetworkRequest header(const char *headerName, const QString &value) &&;
NetworkRequest headerList(const QStringList &headers) &&;
NetworkRequest headerList(
const std::vector<std::pair<QByteArray, QByteArray>> &headers) &&;
NetworkRequest timeout(int ms) &&;
NetworkRequest concurrent() &&;
NetworkRequest authorizeTwitchV5(const QString &clientID,

View file

@ -128,8 +128,8 @@ void uploadImageToNuuls(RawImageData imageData, ChannelPtr channel,
getSettings()->imageUploaderFormField.getValue().isEmpty()
? getSettings()->imageUploaderFormField.getDefaultValue()
: getSettings()->imageUploaderFormField);
QStringList extraHeaders(
getSettings()->imageUploaderHeaders.getValue().split(";"));
auto extraHeaders =
parseHeaderList(getSettings()->imageUploaderHeaders.getValue());
QString originalFilePath = imageData.filePath;
QHttpMultiPart *payload = new QHttpMultiPart(QHttpMultiPart::FormDataType);

View file

@ -9,6 +9,7 @@ set(chatterino_SOURCES
${CMAKE_SOURCE_DIR}/src/common/ChatterinoSetting.cpp
${CMAKE_SOURCE_DIR}/src/common/Modes.cpp
${CMAKE_SOURCE_DIR}/src/common/NetworkCommon.cpp
${CMAKE_SOURCE_DIR}/src/common/NetworkManager.cpp
${CMAKE_SOURCE_DIR}/src/common/NetworkPrivate.cpp
${CMAKE_SOURCE_DIR}/src/common/NetworkRequest.cpp
@ -35,6 +36,7 @@ set(chatterino_SOURCES
set(test_SOURCES
${CMAKE_CURRENT_LIST_DIR}/src/main.cpp
${CMAKE_CURRENT_LIST_DIR}/src/NetworkCommon.cpp
${CMAKE_CURRENT_LIST_DIR}/src/NetworkRequest.cpp
${CMAKE_CURRENT_LIST_DIR}/src/UsernameSet.cpp
${CMAKE_CURRENT_LIST_DIR}/src/HighlightPhrase.cpp

View file

@ -0,0 +1,59 @@
#include "common/NetworkCommon.hpp"
#include <gtest/gtest.h>
using namespace chatterino;
TEST(NetworkCommon, parseHeaderList1)
{
const QString input = "Authorization:secretKey;NextHeader:boo";
const std::vector<std::pair<QByteArray, QByteArray>> expected = {
{"Authorization", "secretKey"},
{"NextHeader", "boo"},
};
const auto actual = parseHeaderList(input);
ASSERT_EQ(expected, actual);
}
TEST(NetworkCommon, parseHeaderListTrimmed)
{
const QString input = "Authorization: secretKey; NextHeader :boo";
const std::vector<std::pair<QByteArray, QByteArray>> expected = {
{"Authorization", "secretKey"},
{"NextHeader", "boo"},
};
const auto actual = parseHeaderList(input);
ASSERT_EQ(expected, actual);
}
TEST(NetworkCommon, parseHeaderListColonInValue)
{
// The input values first header pair contains an invalid value, too many colons. We expect this value to be skipped
const QString input = "Authorization: secretKey:hehe; NextHeader :boo";
const std::vector<std::pair<QByteArray, QByteArray>> expected = {
{"Authorization", "secretKey:hehe"},
{"NextHeader", "boo"},
};
const auto actual = parseHeaderList(input);
ASSERT_EQ(expected, actual);
}
TEST(NetworkCommon, parseHeaderListBadPair)
{
// The input values first header pair doesn't have a colon, so we don't know where the header name and value start/end
const QString input = "Authorization secretKeybad; NextHeader :boo";
const std::vector<std::pair<QByteArray, QByteArray>> expected = {
{"NextHeader", "boo"},
};
const auto actual = parseHeaderList(input);
ASSERT_EQ(expected, actual);
ASSERT_EQ(1, actual.size());
}