diff --git a/loader/include/Geode/utils/web.hpp b/loader/include/Geode/utils/web.hpp index 2dee4074..de85e720 100644 --- a/loader/include/Geode/utils/web.hpp +++ b/loader/include/Geode/utils/web.hpp @@ -126,7 +126,6 @@ namespace geode::utils::web { WebRequest& header(std::string_view name, std::string_view value); WebRequest& param(std::string_view name, std::string_view value); - template <std::integral T> WebRequest& param(std::string_view name, T value) { return this->param(name, std::to_string(value)); @@ -251,5 +250,55 @@ namespace geode::utils::web { * @return WebRequest& */ WebRequest& bodyJSON(matjson::Value const& json); + + + /** + * Gets the request method as a string + * + * @return std::string + */ + std::string getMethod() const; + + /** + * Gets the request URL + * + * @return std::string + */ + std::string getUrl() const; + + /** + * Gets the request headers + * + * @return std::unordered_map<std::string, std::string> + */ + std::unordered_map<std::string, std::string> getHeaders() const; + + /** + * Gets the parameters inside the URL + * + * @return std::unordered_map<std::string, std::string> + */ + std::unordered_map<std::string, std::string> getUrlParams() const; + + /** + * Gets the post body stream + * + * @return std::optional<ByteVector> + */ + std::optional<ByteVector> getBody() const; + + /** + * Gets the request timeout in seconds + * + * @return std::optional<std::chrono::seconds> + */ + std::optional<std::chrono::seconds> getTimeout() const; + + /** + * Gets HTTP versions applied to the request + * + * @return HttpVersion + */ + HttpVersion getHttpVersion() const; }; } diff --git a/loader/src/utils/web.cpp b/loader/src/utils/web.cpp index 9c989184..66405463 100644 --- a/loader/src/utils/web.cpp +++ b/loader/src/utils/web.cpp @@ -279,9 +279,9 @@ WebTask WebRequest::send(std::string_view method, std::string_view url) { // Add parameters to the URL and pass it to curl auto url = impl->m_url; - bool first = true; - for (auto param : impl->m_urlParameters) { - url += (first ? "?" : "&") + urlParamEncode(param.first) + "=" + urlParamEncode(param.second); + bool first = url.find('?') == std::string::npos; + for (auto& [key, value] : impl->m_urlParameters) { + url += (first ? "?" : "&") + urlParamEncode(key) + "=" + urlParamEncode(value); first = false; } curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); @@ -306,6 +306,7 @@ WebTask WebRequest::send(std::string_view method, std::string_view url) { } else if (impl->m_method == "POST") { // curl_easy_perform would freeze on a POST request with no fields, so set it to an empty string // why? god knows + // SMJS: because the stream isn't complete without a body according to the spec curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ""); } @@ -470,13 +471,39 @@ WebTask WebRequest::patch(std::string_view url) { } WebRequest& WebRequest::header(std::string_view name, std::string_view value) { + if (name == "User-Agent") { + userAgent(value); + + return *this; + } if (name == "Accept-Encoding") { + acceptEncoding(value); + + return *this; + } if (name == "Keep-Alive") { + const size_t timeoutPos = value.find("timeout"); + + if (timeoutPos != std::string::npos) { + // At this point idc what happens if I get NPOS or string ends, you shouldn't custom format a spec header + const size_t numStart = value.find('=', timeoutPos) + 1; + const size_t comma = value.find(',', numStart); + const size_t numLength = (comma == std::string::npos ? value.size() : comma) - numStart; + + timeout(std::chrono::seconds(std::stol(std::string(value.substr(numStart, numLength))))); + + return *this; + } + } + m_impl->m_headers.insert_or_assign(std::string(name), std::string(value)); + return *this; } + WebRequest& WebRequest::param(std::string_view name, std::string_view value) { m_impl->m_urlParameters.insert_or_assign(std::string(name), std::string(value)); return *this; } + WebRequest& WebRequest::userAgent(std::string_view name) { m_impl->m_userAgent = name; return *this; @@ -540,3 +567,31 @@ WebRequest& WebRequest::bodyJSON(matjson::Value const& json) { m_impl->m_body = ByteVector { str.begin(), str.end() }; return *this; } + +std::string WebRequest::getMethod() const { + return m_impl->m_method; +} + +std::string WebRequest::getUrl() const { + return m_impl->m_url; +} + +std::unordered_map<std::string, std::string> WebRequest::getHeaders() const { + return m_impl->m_headers; +} + +std::unordered_map<std::string, std::string> WebRequest::getUrlParams() const { + return m_impl->m_urlParameters; +} + +std::optional<ByteVector> WebRequest::getBody() const { + return m_impl->m_body; +} + +std::optional<std::chrono::seconds> WebRequest::getTimeout() const { + return m_impl->m_timeout; +} + +HttpVersion WebRequest::getHttpVersion() const { + return m_impl->m_httpVersion; +} \ No newline at end of file