aboutsummaryrefslogtreecommitdiff
path: root/src/filesystem/DllLibCurl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/filesystem/DllLibCurl.cpp')
-rw-r--r--src/filesystem/DllLibCurl.cpp329
1 files changed, 329 insertions, 0 deletions
diff --git a/src/filesystem/DllLibCurl.cpp b/src/filesystem/DllLibCurl.cpp
new file mode 100644
index 0000000000..62c8ce2c12
--- /dev/null
+++ b/src/filesystem/DllLibCurl.cpp
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "threads/SystemClock.h"
+#include "system.h"
+#include "DllLibCurl.h"
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+#include "utils/TimeUtils.h"
+
+#include <assert.h>
+
+#ifdef HAVE_OPENSSL
+#include "threads/Thread.h"
+#include "openssl/crypto.h"
+
+static CCriticalSection** m_sslLockArray = NULL;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+void ssl_lock_callback(int mode, int type, char *file, int line)
+{
+ if (!m_sslLockArray)
+ return;
+
+ if (mode & CRYPTO_LOCK)
+ m_sslLockArray[type]->lock();
+ else
+ m_sslLockArray[type]->unlock();
+}
+
+unsigned long ssl_thread_id(void)
+{
+ return (unsigned long)CThread::GetCurrentThreadId();
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // HAVE_OPENSSL
+
+using namespace XCURL;
+
+/* okey this is damn ugly. our dll loader doesn't allow for postload, preunload functions */
+static long g_curlReferences = 0;
+#if(0)
+static unsigned int g_curlTimeout = 0;
+#endif
+
+bool DllLibCurlGlobal::Load()
+{
+ CSingleLock lock(m_critSection);
+ if(g_curlReferences > 0)
+ {
+ g_curlReferences++;
+ return true;
+ }
+
+ /* we handle this ourself */
+ DllDynamic::EnableDelayedUnload(false);
+ if (!DllDynamic::Load())
+ return false;
+
+ if (global_init(CURL_GLOBAL_ALL))
+ {
+ DllDynamic::Unload();
+ CLog::Log(LOGERROR, "Error initializing libcurl");
+ return false;
+ }
+
+ /* check idle will clean up the last one */
+ g_curlReferences = 2;
+
+#if defined(HAS_CURL_STATIC)
+ // Initialize ssl locking array
+ m_sslLockArray = new CCriticalSection*[CRYPTO_num_locks()];
+ for (int i=0; i<CRYPTO_num_locks(); i++)
+ m_sslLockArray[i] = new CCriticalSection;
+
+ crypto_set_id_callback((unsigned long (*)())ssl_thread_id);
+ crypto_set_locking_callback((void (*)(int, int, const char*, int))ssl_lock_callback);
+#endif
+
+ return true;
+}
+
+void DllLibCurlGlobal::Unload()
+{
+ CSingleLock lock(m_critSection);
+ if (--g_curlReferences == 0)
+ {
+ if (!IsLoaded())
+ return;
+
+ // close libcurl
+ global_cleanup();
+
+#if defined(HAS_CURL_STATIC)
+ // Cleanup ssl locking array
+ crypto_set_id_callback(NULL);
+ crypto_set_locking_callback(NULL);
+ for (int i=0; i<CRYPTO_num_locks(); i++)
+ delete m_sslLockArray[i];
+
+ delete[] m_sslLockArray;
+#endif
+
+ DllDynamic::Unload();
+ }
+
+ /* CheckIdle will clear this one up */
+#if(0)
+ if(g_curlReferences == 1)
+ g_curlTimeout = XbmcThreads::SystemClockMillis();
+#endif
+}
+
+void DllLibCurlGlobal::CheckIdle()
+{
+ /* avoid locking section here, to avoid stalling gfx thread on loads*/
+ if(g_curlReferences == 0)
+ return;
+
+ CSingleLock lock(m_critSection);
+ /* 20 seconds idle time before closing handle */
+ const unsigned int idletime = 30000;
+
+ VEC_CURLSESSIONS::iterator it = m_sessions.begin();
+ while(it != m_sessions.end())
+ {
+ if( !it->m_busy && (XbmcThreads::SystemClockMillis() - it->m_idletimestamp) > idletime )
+ {
+ CLog::Log(LOGINFO, "%s - Closing session to %s://%s (easy=%p, multi=%p)\n", __FUNCTION__, it->m_protocol.c_str(), it->m_hostname.c_str(), (void*)it->m_easy, (void*)it->m_multi);
+
+ // It's important to clean up multi *before* cleaning up easy, because the multi cleanup
+ // code accesses stuff in the easy's structure.
+ if(it->m_multi)
+ multi_cleanup(it->m_multi);
+ if(it->m_easy)
+ easy_cleanup(it->m_easy);
+
+ Unload();
+
+ it = m_sessions.erase(it);
+ continue;
+ }
+ it++;
+ }
+
+ /* check if we should unload the dll */
+#if(0) // we never unload libcurl, since libssl can break when python unloads then
+ if(g_curlReferences == 1 && XbmcThreads::SystemClockMillis() - g_curlTimeout > idletime)
+ Unload();
+#endif
+}
+
+void DllLibCurlGlobal::easy_aquire(const char *protocol, const char *hostname, CURL_HANDLE** easy_handle, CURLM** multi_handle)
+{
+ assert(easy_handle != NULL);
+
+ CSingleLock lock(m_critSection);
+
+ VEC_CURLSESSIONS::iterator it;
+ for(it = m_sessions.begin(); it != m_sessions.end(); it++)
+ {
+ if( !it->m_busy )
+ {
+ /* allow reuse of requester is trying to connect to same host */
+ /* curl will take care of any differences in username/password */
+ if( it->m_protocol.compare(protocol) == 0 && it->m_hostname.compare(hostname) == 0)
+ {
+ it->m_busy = true;
+ if(easy_handle)
+ {
+ if(!it->m_easy)
+ it->m_easy = easy_init();
+
+ *easy_handle = it->m_easy;
+ }
+
+ if(multi_handle)
+ {
+ if(!it->m_multi)
+ it->m_multi = multi_init();
+
+ *multi_handle = it->m_multi;
+ }
+
+ return;
+ }
+ }
+ }
+
+ SSession session = {};
+ session.m_busy = true;
+ session.m_protocol = protocol;
+ session.m_hostname = hostname;
+
+ /* count up global interface counter */
+ Load();
+
+ if(easy_handle)
+ {
+ session.m_easy = easy_init();
+ *easy_handle = session.m_easy;
+ }
+
+ if(multi_handle)
+ {
+ session.m_multi = multi_init();
+ *multi_handle = session.m_multi;
+ }
+
+ m_sessions.push_back(session);
+
+
+ CLog::Log(LOGINFO, "%s - Created session to %s://%s\n", __FUNCTION__, protocol, hostname);
+
+ return;
+
+}
+
+void DllLibCurlGlobal::easy_release(CURL_HANDLE** easy_handle, CURLM** multi_handle)
+{
+ CSingleLock lock(m_critSection);
+
+ CURL_HANDLE* easy = NULL;
+ CURLM* multi = NULL;
+
+ if(easy_handle)
+ {
+ easy = *easy_handle;
+ *easy_handle = NULL;
+ }
+
+ if(multi_handle)
+ {
+ multi = *multi_handle;
+ *multi_handle = NULL;
+ }
+
+ VEC_CURLSESSIONS::iterator it;
+ for(it = m_sessions.begin(); it != m_sessions.end(); it++)
+ {
+ if( it->m_easy == easy && (multi == NULL || it->m_multi == multi) )
+ {
+ /* reset session so next caller doesn't reuse options, only connections */
+ /* will reset verbose too so it won't print that it closed connections on cleanup*/
+ easy_reset(easy);
+ it->m_busy = false;
+ it->m_idletimestamp = XbmcThreads::SystemClockMillis();
+ return;
+ }
+ }
+}
+
+CURL_HANDLE* DllLibCurlGlobal::easy_duphandle(CURL_HANDLE* easy_handle)
+{
+ CSingleLock lock(m_critSection);
+
+ VEC_CURLSESSIONS::iterator it;
+ for(it = m_sessions.begin(); it != m_sessions.end(); it++)
+ {
+ if( it->m_easy == easy_handle )
+ {
+ SSession session = *it;
+ session.m_easy = DllLibCurl::easy_duphandle(easy_handle);
+ Load();
+ m_sessions.push_back(session);
+ return session.m_easy;
+ }
+ }
+ return DllLibCurl::easy_duphandle(easy_handle);
+}
+
+void DllLibCurlGlobal::easy_duplicate(CURL_HANDLE* easy, CURLM* multi, CURL_HANDLE** easy_out, CURLM** multi_out)
+{
+ CSingleLock lock(m_critSection);
+
+ if(easy_out && easy)
+ *easy_out = DllLibCurl::easy_duphandle(easy);
+
+ if(multi_out && multi)
+ *multi_out = DllLibCurl::multi_init();
+
+ VEC_CURLSESSIONS::iterator it;
+ for(it = m_sessions.begin(); it != m_sessions.end(); it++)
+ {
+ if( it->m_easy == easy )
+ {
+ SSession session = *it;
+ if(easy_out && easy)
+ session.m_easy = *easy_out;
+ else
+ session.m_easy = NULL;
+
+ if(multi_out && multi)
+ session.m_multi = *multi_out;
+ else
+ session.m_multi = NULL;
+
+ Load();
+ m_sessions.push_back(session);
+ return;
+ }
+ }
+ return;
+}