Web support for multiple headers with same name (#1150)

Used for Set-Cookie, for example
This commit is contained in:
B1rtek 2024-11-15 16:40:11 +01:00 committed by GitHub
parent fb504cbf83
commit 64d9a289a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 41 additions and 12 deletions

View file

@ -83,6 +83,15 @@ namespace geode::utils::web {
std::vector<std::string> headers() const; std::vector<std::string> headers() const;
std::optional<std::string> header(std::string_view name) const; std::optional<std::string> header(std::string_view name) const;
/**
* Retrieves a list of all headers from the response with a given name - there can be
* multiple headers with the same name, such as Set-Cookie, with each cookie in a separate
* header
* @param name name of the header
* @return std::optional<std::vector<std::string>>
*/
std::optional<std::vector<std::string>> getAllHeadersNamed(std::string_view name) const;
}; };
class GEODE_DLL WebProgress final { class GEODE_DLL WebProgress final {
@ -286,9 +295,9 @@ namespace geode::utils::web {
/** /**
* Gets the request headers * Gets the request headers
* *
* @return std::unordered_map<std::string, std::string> * @return std::unordered_map<std::string, std::vector<std::string>>
*/ */
std::unordered_map<std::string, std::string> getHeaders() const; std::unordered_map<std::string, std::vector<std::string>> getHeaders() const;
/** /**
* Gets the parameters inside the URL * Gets the parameters inside the URL

View file

@ -96,7 +96,7 @@ class WebResponse::Impl {
public: public:
int m_code; int m_code;
ByteVector m_data; ByteVector m_data;
std::unordered_map<std::string, std::string> m_headers; std::unordered_map<std::string, std::vector<std::string>> m_headers;
Result<> into(std::filesystem::path const& path) const; Result<> into(std::filesystem::path const& path) const;
}; };
@ -146,8 +146,14 @@ std::vector<std::string> WebResponse::headers() const {
} }
std::optional<std::string> WebResponse::header(std::string_view name) const { std::optional<std::string> WebResponse::header(std::string_view name) const {
auto str = std::string(name); if (auto str = std::string(name); m_impl->m_headers.contains(str)) {
if (m_impl->m_headers.contains(str)) { return m_impl->m_headers.at(str).at(0);
}
return std::nullopt;
}
std::optional<std::vector<std::string>> WebResponse::getAllHeadersNamed(std::string_view name) const {
if (auto str = std::string(name); m_impl->m_headers.contains(str)) {
return m_impl->m_headers.at(str); return m_impl->m_headers.at(str);
} }
return std::nullopt; return std::nullopt;
@ -189,7 +195,7 @@ public:
std::string m_method; std::string m_method;
std::string m_url; std::string m_url;
std::unordered_map<std::string, std::string> m_headers; std::unordered_map<std::string, std::vector<std::string>> m_headers;
std::unordered_map<std::string, std::string> m_urlParameters; std::unordered_map<std::string, std::string> m_urlParameters;
std::optional<std::string> m_userAgent; std::optional<std::string> m_userAgent;
std::optional<std::string> m_acceptEncodingType; std::optional<std::string> m_acceptEncodingType;
@ -270,15 +276,17 @@ WebTask WebRequest::send(std::string_view method, std::string_view url) {
// Set headers // Set headers
curl_slist* headers = nullptr; curl_slist* headers = nullptr;
for (auto& [name, value] : impl->m_headers) { for (auto& [name, values] : impl->m_headers) {
// Sanitize header name // Sanitize header name
auto header = name; auto header = name;
header.erase(std::remove_if(header.begin(), header.end(), [](char c) { header.erase(std::remove_if(header.begin(), header.end(), [](char c) {
return c == '\r' || c == '\n'; return c == '\r' || c == '\n';
}), header.end()); }), header.end());
// Append value // Append value
header += ": " + value; for (const auto& value: values) {
headers = curl_slist_append(headers, header.c_str()); header += ": " + value;
headers = curl_slist_append(headers, header.c_str());
}
} }
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
@ -406,7 +414,12 @@ WebTask WebRequest::send(std::string_view method, std::string_view url) {
if (value.ends_with('\r')) { if (value.ends_with('\r')) {
value = value.substr(0, value.size() - 1); value = value.substr(0, value.size() - 1);
} }
headers.insert_or_assign(key, value); // Create a new vector and add to it or add to an already existing one
if (headers.contains(key)) {
headers.at(key).push_back(value);
} else {
headers.insert_or_assign(key, std::vector{value});
}
} }
return size * nitems; return size * nitems;
})); }));
@ -508,7 +521,14 @@ WebRequest& WebRequest::header(std::string_view name, std::string_view value) {
} }
} }
m_impl->m_headers.insert_or_assign(std::string(name), std::string(value)); // Create a new vector and add to it or add to an already existing one
std::string strName = std::string(name);
std::string strValue = std::string(value);
if (m_impl->m_headers.contains(strName)) {
m_impl->m_headers.at(strName).push_back(strValue);
} else {
m_impl->m_headers.insert_or_assign(strName, std::vector{strValue});
}
return *this; return *this;
} }
@ -609,7 +629,7 @@ std::string WebRequest::getUrl() const {
return m_impl->m_url; return m_impl->m_url;
} }
std::unordered_map<std::string, std::string> WebRequest::getHeaders() const { std::unordered_map<std::string, std::vector<std::string>> WebRequest::getHeaders() const {
return m_impl->m_headers; return m_impl->m_headers;
} }