2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "DllLibCurl.h"
11 #include "threads/SystemClock.h"
12 #include "utils/log.h"
19 CURLcode
DllLibCurl::global_init(long flags
)
21 return curl_global_init(flags
);
24 void DllLibCurl::global_cleanup()
26 curl_global_cleanup();
29 CURL_HANDLE
* DllLibCurl::easy_init()
31 return curl_easy_init();
34 CURLcode
DllLibCurl::easy_perform(CURL_HANDLE
* handle
)
36 return curl_easy_perform(handle
);
39 CURLcode
DllLibCurl::easy_pause(CURL_HANDLE
* handle
, int bitmask
)
41 return curl_easy_pause(handle
, bitmask
);
44 void DllLibCurl::easy_reset(CURL_HANDLE
* handle
)
46 curl_easy_reset(handle
);
49 void DllLibCurl::easy_cleanup(CURL_HANDLE
* handle
)
51 curl_easy_cleanup(handle
);
54 CURL_HANDLE
* DllLibCurl::easy_duphandle(CURL_HANDLE
* handle
)
56 return curl_easy_duphandle(handle
);
59 CURLM
* DllLibCurl::multi_init()
61 return curl_multi_init();
64 CURLMcode
DllLibCurl::multi_add_handle(CURLM
* multi_handle
, CURL_HANDLE
* easy_handle
)
66 return curl_multi_add_handle(multi_handle
, easy_handle
);
69 CURLMcode
DllLibCurl::multi_perform(CURLM
* multi_handle
, int* running_handles
)
71 return curl_multi_perform(multi_handle
, running_handles
);
74 CURLMcode
DllLibCurl::multi_remove_handle(CURLM
* multi_handle
, CURL_HANDLE
* easy_handle
)
76 return curl_multi_remove_handle(multi_handle
, easy_handle
);
79 CURLMcode
DllLibCurl::multi_fdset(
80 CURLM
* multi_handle
, fd_set
* read_fd_set
, fd_set
* write_fd_set
, fd_set
* exc_fd_set
, int* max_fd
)
82 return curl_multi_fdset(multi_handle
, read_fd_set
, write_fd_set
, exc_fd_set
, max_fd
);
85 CURLMcode
DllLibCurl::multi_timeout(CURLM
* multi_handle
, long* timeout
)
87 return curl_multi_timeout(multi_handle
, timeout
);
90 CURLMsg
* DllLibCurl::multi_info_read(CURLM
* multi_handle
, int* msgs_in_queue
)
92 return curl_multi_info_read(multi_handle
, msgs_in_queue
);
95 CURLMcode
DllLibCurl::multi_cleanup(CURLM
* handle
)
97 return curl_multi_cleanup(handle
);
100 curl_slist
* DllLibCurl::slist_append(curl_slist
* list
, const char* to_append
)
102 return curl_slist_append(list
, to_append
);
105 void DllLibCurl::slist_free_all(curl_slist
* list
)
107 curl_slist_free_all(list
);
110 const char* DllLibCurl::easy_strerror(CURLcode code
)
112 return curl_easy_strerror(code
);
115 DllLibCurlGlobal::DllLibCurlGlobal()
117 /* we handle this ourself */
118 if (curl_global_init(CURL_GLOBAL_ALL
))
120 CLog::Log(LOGERROR
, "Error initializing libcurl");
124 DllLibCurlGlobal::~DllLibCurlGlobal()
126 for (auto& session
: m_sessions
)
128 if (session
.m_multi
&& session
.m_easy
)
129 multi_remove_handle(session
.m_multi
, session
.m_easy
);
131 easy_cleanup(session
.m_easy
);
133 multi_cleanup(session
.m_multi
);
136 curl_global_cleanup();
139 void DllLibCurlGlobal::CheckIdle()
141 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
142 /* 20 seconds idle time before closing handle */
143 const unsigned int idletime
= 30000;
145 VEC_CURLSESSIONS::iterator it
= m_sessions
.begin();
146 while (it
!= m_sessions
.end())
148 auto now
= std::chrono::steady_clock::now();
150 std::chrono::duration_cast
<std::chrono::milliseconds
>(now
- it
->m_idletimestamp
);
152 if (!it
->m_busy
&& duration
.count() > idletime
)
154 CLog::Log(LOGDEBUG
, "{} - Closing session to {}://{} (easy={}, multi={})", __FUNCTION__
,
155 it
->m_protocol
, it
->m_hostname
, fmt::ptr(it
->m_easy
), fmt::ptr(it
->m_multi
));
157 if (it
->m_multi
&& it
->m_easy
)
158 multi_remove_handle(it
->m_multi
, it
->m_easy
);
160 easy_cleanup(it
->m_easy
);
162 multi_cleanup(it
->m_multi
);
164 it
= m_sessions
.erase(it
);
171 void DllLibCurlGlobal::easy_acquire(const char* protocol
,
172 const char* hostname
,
173 CURL_HANDLE
** easy_handle
,
174 CURLM
** multi_handle
)
176 assert(easy_handle
!= NULL
);
178 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
180 for (auto& it
: m_sessions
)
184 /* allow reuse of requester is trying to connect to same host */
185 /* curl will take care of any differences in username/password */
186 if (it
.m_protocol
.compare(protocol
) == 0 && it
.m_hostname
.compare(hostname
) == 0)
192 it
.m_easy
= easy_init();
194 *easy_handle
= it
.m_easy
;
200 it
.m_multi
= multi_init();
202 *multi_handle
= it
.m_multi
;
210 SSession session
= {};
211 session
.m_busy
= true;
212 session
.m_protocol
= protocol
;
213 session
.m_hostname
= hostname
;
217 session
.m_easy
= easy_init();
218 *easy_handle
= session
.m_easy
;
223 session
.m_multi
= multi_init();
224 *multi_handle
= session
.m_multi
;
227 m_sessions
.push_back(session
);
229 CLog::Log(LOGDEBUG
, "{} - Created session to {}://{}", __FUNCTION__
, protocol
, hostname
);
232 void DllLibCurlGlobal::easy_release(CURL_HANDLE
** easy_handle
, CURLM
** multi_handle
)
234 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
236 CURL_HANDLE
* easy
= NULL
;
247 multi
= *multi_handle
;
248 *multi_handle
= NULL
;
251 for (auto& it
: m_sessions
)
253 if (it
.m_easy
== easy
&& (multi
== nullptr || it
.m_multi
== multi
))
255 /* reset session so next caller doesn't reuse options, only connections */
256 /* will reset verbose too so it won't print that it closed connections on cleanup*/
259 it
.m_idletimestamp
= std::chrono::steady_clock::now();
265 CURL_HANDLE
* DllLibCurlGlobal::easy_duphandle(CURL_HANDLE
* easy_handle
)
267 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
269 for (const auto& it
: m_sessions
)
271 if (it
.m_easy
== easy_handle
)
273 SSession session
= it
;
274 session
.m_easy
= DllLibCurl::easy_duphandle(easy_handle
);
275 m_sessions
.push_back(session
);
276 return session
.m_easy
;
279 return DllLibCurl::easy_duphandle(easy_handle
);
282 void DllLibCurlGlobal::easy_duplicate(CURL_HANDLE
* easy
,
284 CURL_HANDLE
** easy_out
,
287 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
289 if (easy_out
&& easy
)
290 *easy_out
= DllLibCurl::easy_duphandle(easy
);
292 if (multi_out
&& multi
)
293 *multi_out
= DllLibCurl::multi_init();
295 for (const auto& it
: m_sessions
)
297 if (it
.m_easy
== easy
)
299 SSession session
= it
;
300 if (easy_out
&& easy
)
301 session
.m_easy
= *easy_out
;
303 session
.m_easy
= NULL
;
305 if (multi_out
&& multi
)
306 session
.m_multi
= *multi_out
;
308 session
.m_multi
= NULL
;
310 m_sessions
.push_back(session
);