Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #define FORBIDDEN_SYMBOL_ALLOW_ALL
00024
00025 #include <curl/curl.h>
00026 #include "backends/networking/curl/connectionmanager.h"
00027 #include "backends/networking/curl/networkreadstream.h"
00028 #include "common/debug.h"
00029 #include "common/system.h"
00030 #include "common/timer.h"
00031
00032 namespace Common {
00033
00034 DECLARE_SINGLETON(Networking::ConnectionManager);
00035
00036 }
00037
00038 namespace Networking {
00039
00040 ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false), _frame(0) {
00041 curl_global_init(CURL_GLOBAL_ALL);
00042 _multi = curl_multi_init();
00043 }
00044
00045 ConnectionManager::~ConnectionManager() {
00046 stopTimer();
00047
00048
00049 _handleMutex.lock();
00050 for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end(); ++i) {
00051 Request *request = i->request;
00052 RequestCallback callback = i->onDeleteCallback;
00053 if (request)
00054 request->finish();
00055 delete request;
00056 if (callback)
00057 (*callback)(request);
00058 }
00059 _requests.clear();
00060
00061
00062 curl_multi_cleanup(_multi);
00063 curl_global_cleanup();
00064 _multi = nullptr;
00065 _handleMutex.unlock();
00066 }
00067
00068 void ConnectionManager::registerEasyHandle(CURL *easy) const {
00069 curl_multi_add_handle(_multi, easy);
00070 }
00071
00072 Request *ConnectionManager::addRequest(Request *request, RequestCallback callback) {
00073 _addedRequestsMutex.lock();
00074 _addedRequests.push_back(RequestWithCallback(request, callback));
00075 if (!_timerStarted)
00076 startTimer();
00077 _addedRequestsMutex.unlock();
00078 return request;
00079 }
00080
00081 Common::String ConnectionManager::urlEncode(Common::String s) const {
00082 if (!_multi)
00083 return "";
00084 #if LIBCURL_VERSION_NUM >= 0x070F04
00085 char *output = curl_easy_escape(_multi, s.c_str(), s.size());
00086 #else
00087 char *output = curl_escape(s.c_str(), s.size());
00088 #endif
00089 if (output) {
00090 Common::String result = output;
00091 curl_free(output);
00092 return result;
00093 }
00094 return "";
00095 }
00096
00097 uint32 ConnectionManager::getCloudRequestsPeriodInMicroseconds() {
00098 return TIMER_INTERVAL * CLOUD_PERIOD;
00099 }
00100
00101
00102
00103 void connectionsThread(void *ignored) {
00104 ConnMan.handle();
00105 }
00106
00107 void ConnectionManager::startTimer(int interval) {
00108 Common::TimerManager *manager = g_system->getTimerManager();
00109 if (manager->installTimerProc(connectionsThread, interval, 0, "Networking::ConnectionManager's Timer")) {
00110 _timerStarted = true;
00111 } else {
00112 warning("Failed to install Networking::ConnectionManager's timer");
00113 }
00114 }
00115
00116 void ConnectionManager::stopTimer() {
00117 debug(9, "timer stopped");
00118 Common::TimerManager *manager = g_system->getTimerManager();
00119 manager->removeTimerProc(connectionsThread);
00120 _timerStarted = false;
00121 }
00122
00123 bool ConnectionManager::hasAddedRequests() {
00124 _addedRequestsMutex.lock();
00125 bool hasNewRequests = !_addedRequests.empty();
00126 _addedRequestsMutex.unlock();
00127 return hasNewRequests;
00128 }
00129
00130 void ConnectionManager::handle() {
00131
00132 _handleMutex.lock();
00133 ++_frame;
00134 if (_frame % CLOUD_PERIOD == 0)
00135 interateRequests();
00136 if (_frame % CURL_PERIOD == 0)
00137 processTransfers();
00138
00139 if (_requests.empty() && !hasAddedRequests())
00140 stopTimer();
00141 _handleMutex.unlock();
00142 }
00143
00144 void ConnectionManager::interateRequests() {
00145
00146 _addedRequestsMutex.lock();
00147 for (Common::Array<RequestWithCallback>::iterator i = _addedRequests.begin(); i != _addedRequests.end(); ++i) {
00148 _requests.push_back(*i);
00149 }
00150 _addedRequests.clear();
00151 _addedRequestsMutex.unlock();
00152
00153
00154 debug(9, "handling %d request(s)", _requests.size());
00155 for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end();) {
00156 Request *request = i->request;
00157 if (request) {
00158 if (request->state() == PROCESSING)
00159 request->handle();
00160 else if (request->state() == RETRY)
00161 request->handleRetry();
00162 }
00163
00164 if (!request || request->state() == FINISHED) {
00165 delete (i->request);
00166 if (i->onDeleteCallback)
00167 (*i->onDeleteCallback)(i->request);
00168 _requests.erase(i);
00169 continue;
00170 }
00171
00172 ++i;
00173 }
00174 }
00175
00176 void ConnectionManager::processTransfers() {
00177 if (!_multi) return;
00178
00179
00180 int transfersRunning;
00181 curl_multi_perform(_multi, &transfersRunning);
00182
00183 int messagesInQueue;
00184 CURLMsg *curlMsg;
00185 while ((curlMsg = curl_multi_info_read(_multi, &messagesInQueue))) {
00186 CURL *easyHandle = curlMsg->easy_handle;
00187
00188 NetworkReadStream *stream;
00189 curl_easy_getinfo(easyHandle, CURLINFO_PRIVATE, &stream);
00190 if (stream)
00191 stream->finished();
00192
00193 if (curlMsg->msg == CURLMSG_DONE) {
00194 debug(9, "ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result));
00195 } else {
00196 warning("ConnectionManager: FAILURE (CURLMsg (%d))", curlMsg->msg);
00197 }
00198
00199 curl_multi_remove_handle(_multi, easyHandle);
00200 }
00201 }
00202
00203 }