@@ -34,18 +34,21 @@ class HttpRequest::Impl final
3434 s32 GetLastResponseCode ();
3535 Response Fetch (const std::string& url, Method method, const Headers& headers, const u8 * payload,
3636 size_t size, AllowedReturnCodes codes = AllowedReturnCodes::Ok_Only,
37- std::span <Multiform> multiform = {});
37+ const std::vector <Multiform>& multiform = {});
3838
3939 static int CurlProgressCallback (Impl* impl, curl_off_t dltotal, curl_off_t dlnow,
4040 curl_off_t ultotal, curl_off_t ulnow);
4141 std::string EscapeComponent (const std::string& string);
42+ std::string GetFinalUrl () const { return m_final_url; }
4243
44+ void SetProgressCallback (ProgressCallback callback);
4345private:
4446 static inline std::once_flag s_curl_was_initialized;
4547 ProgressCallback m_callback;
4648 Headers m_response_headers;
4749 std::unique_ptr<CURL, decltype (&curl_easy_cleanup)> m_curl{nullptr , curl_easy_cleanup};
4850 std::string m_error_string;
51+ std::string m_final_url;
4952};
5053
5154HttpRequest::HttpRequest (std::chrono::milliseconds timeout_ms, ProgressCallback callback)
@@ -55,6 +58,15 @@ HttpRequest::HttpRequest(std::chrono::milliseconds timeout_ms, ProgressCallback
5558
5659HttpRequest::~HttpRequest () = default ;
5760
61+ void HttpRequest::SetProgressCallback (ProgressCallback callback)
62+ {
63+ m_callback = std::move (callback);
64+ if (m_impl)
65+ {
66+ m_impl->SetProgressCallback (m_callback);
67+ }
68+ }
69+
5870bool HttpRequest::IsValid () const
5971{
6072 return m_impl->IsValid ();
@@ -115,8 +127,8 @@ int HttpRequest::Impl::CurlProgressCallback(Impl* impl, curl_off_t dltotal, curl
115127 // Call the progress callback with the current download progress
116128 if (impl->m_callback )
117129 {
118- return ! impl->m_callback (static_cast <s64>(dltotal), static_cast <s64>(dlnow),
119- static_cast <s64>(ultotal), static_cast <s64>(ulnow));
130+ return impl->m_callback (static_cast <s64>(dltotal), static_cast <s64>(dlnow),
131+ static_cast <s64>(ultotal), static_cast <s64>(ulnow)) ? 0 : 1 ;
120132 }
121133 return 0 ; // If no callback, continue
122134}
@@ -183,7 +195,7 @@ void HttpRequest::Impl::UseIPv4()
183195}
184196
185197HttpRequest::Response HttpRequest::PostMultiform (const std::string& url,
186- std::span <Multiform> multiform,
198+ const std::vector <Multiform>& multiform,
187199 const Headers& headers, AllowedReturnCodes codes)
188200{
189201 return m_impl->Fetch (url, Impl::Method::POST, headers, nullptr , 0 , codes, multiform);
@@ -195,6 +207,20 @@ void HttpRequest::Impl::FollowRedirects(long max)
195207 curl_easy_setopt (m_curl.get (), CURLOPT_MAXREDIRS, max);
196208}
197209
210+ void HttpRequest::Impl::SetProgressCallback (ProgressCallback callback)
211+ {
212+ m_callback = std::move (callback);
213+ if (m_curl)
214+ {
215+ curl_easy_setopt (m_curl.get (), CURLOPT_NOPROGRESS, m_callback == nullptr );
216+ if (m_callback)
217+ {
218+ curl_easy_setopt (m_curl.get (), CURLOPT_PROGRESSDATA, this );
219+ curl_easy_setopt (m_curl.get (), CURLOPT_XFERINFOFUNCTION, CurlProgressCallback);
220+ }
221+ }
222+ }
223+
198224std::string HttpRequest::Impl::GetHeaderValue (std::string_view name) const
199225{
200226 for (const auto & [key, value] : m_response_headers)
@@ -241,12 +267,12 @@ static size_t header_callback(char* buffer, size_t size, size_t nitems, void* us
241267HttpRequest::Response HttpRequest::Impl::Fetch (const std::string& url, Method method,
242268 const Headers& headers, const u8 * payload,
243269 size_t size, AllowedReturnCodes codes,
244- std::span <Multiform> multiform)
270+ const std::vector <Multiform>& multiform)
245271{
246272 m_response_headers.clear ();
247- curl_easy_setopt (m_curl.get (), CURLOPT_POST, method == Method::POST);
273+ curl_easy_setopt (m_curl.get (), CURLOPT_POST, method == Impl:: Method::POST);
248274 curl_easy_setopt (m_curl.get (), CURLOPT_URL, url.c_str ());
249- if (method == Method::POST && multiform.empty ())
275+ if (method == Impl:: Method::POST && multiform.empty ())
250276 {
251277 curl_easy_setopt (m_curl.get (), CURLOPT_POSTFIELDS, payload);
252278 curl_easy_setopt (m_curl.get (), CURLOPT_POSTFIELDSIZE, size);
@@ -273,9 +299,9 @@ HttpRequest::Response HttpRequest::Impl::Fetch(const std::string& url, Method me
273299 {
274300 if (!value)
275301 list = curl_slist_append (list, (name + ' :' ).c_str ());
276- else if (value->empty ())
302+ else if (value && value ->empty ())
277303 list = curl_slist_append (list, (name + ' ;' ).c_str ());
278- else
304+ else if (value)
279305 list = curl_slist_append (list, (name + " : " + *value).c_str ());
280306 }
281307
@@ -290,14 +316,20 @@ HttpRequest::Response HttpRequest::Impl::Fetch(const std::string& url, Method me
290316 curl_easy_setopt (m_curl.get (), CURLOPT_WRITEFUNCTION, CurlWriteCallback);
291317 curl_easy_setopt (m_curl.get (), CURLOPT_WRITEDATA, &buffer);
292318
293- const char * type = method == Method::POST ? " POST" : " GET" ;
319+ const char * type = method == Impl:: Method::POST ? " POST" : " GET" ;
294320 const CURLcode res = curl_easy_perform (m_curl.get ());
295321 if (res != CURLE_OK)
296322 {
297323 ERROR_LOG_FMT (COMMON, " Failed to {} {}: {}" , type, url, m_error_string);
298324 return {};
299325 }
300326
327+ // Store the final URL after redirects
328+ char * final_url = nullptr ;
329+ if (curl_easy_getinfo (m_curl.get (), CURLINFO_EFFECTIVE_URL, &final_url) == CURLE_OK && final_url)
330+ m_final_url = final_url;
331+ else
332+ m_final_url.clear ();
301333 if (codes == AllowedReturnCodes::All)
302334 return buffer;
303335
@@ -321,4 +353,10 @@ HttpRequest::Response HttpRequest::Impl::Fetch(const std::string& url, Method me
321353
322354 return buffer;
323355}
356+
357+ std::string HttpRequest::GetFinalUrl () const
358+ {
359+ return m_impl->GetFinalUrl ();
360+ }
361+
324362} // namespace Common
0 commit comments