CLOUD: Make ConnectionManager singleton

With ConnectionManager singleton one can start their Requests without
creating Storage instance. Moreover, Storage instance should contain
cloud API, not Requests-related handling and timer starting methods.
Thus, these methods were moved into ConnectionManager itself.
This commit is contained in:
Alexander Tkachev 2016-05-18 15:21:09 +06:00
parent e743a65636
commit 0a947618fb
12 changed files with 123 additions and 139 deletions

View file

@ -22,6 +22,7 @@
#define FORBIDDEN_SYMBOL_ALLOW_ALL #define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "backends/cloud/dropbox/dropboxstorage.h" #include "backends/cloud/dropbox/dropboxstorage.h"
#include "backends/networking/curl/connectionmanager.h"
#include "backends/networking/curl/curljsonrequest.h" #include "backends/networking/curl/curljsonrequest.h"
#include "common/config-manager.h" #include "common/config-manager.h"
#include "common/debug.h" #include "common/debug.h"
@ -77,7 +78,6 @@ DropboxStorage::~DropboxStorage() {
} }
void DropboxStorage::listDirectory(Common::String path) { void DropboxStorage::listDirectory(Common::String path) {
startTimer(1000000); //in one second
} }
void DropboxStorage::syncSaves() { void DropboxStorage::syncSaves() {
@ -88,7 +88,7 @@ void DropboxStorage::syncSaves() {
void DropboxStorage::printInfo() { void DropboxStorage::printInfo() {
Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(printJsonCallback, "https://api.dropboxapi.com/1/account/info"); Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(printJsonCallback, "https://api.dropboxapi.com/1/account/info");
request->addHeader("Authorization: Bearer " + _token); request->addHeader("Authorization: Bearer " + _token);
addRequest(request); ConnMan.addRequest(request);
} }
DropboxStorage *DropboxStorage::loadFromConfig() { DropboxStorage *DropboxStorage::loadFromConfig() {
@ -119,10 +119,10 @@ Common::String DropboxStorage::getAuthLink() {
return url; return url;
} }
DropboxStorage *DropboxStorage::authThroughConsole() { void DropboxStorage::authThroughConsole() {
if (!ConfMan.hasKey("DROPBOX_KEY", "cloud") || !ConfMan.hasKey("DROPBOX_SECRET", "cloud")) { if (!ConfMan.hasKey("DROPBOX_KEY", "cloud") || !ConfMan.hasKey("DROPBOX_SECRET", "cloud")) {
warning("No Dropbox keys available, cannot do auth"); warning("No Dropbox keys available, cannot do auth");
return 0; return;
} }
KEY = ConfMan.get("DROPBOX_KEY", "cloud"); KEY = ConfMan.get("DROPBOX_KEY", "cloud");
@ -130,7 +130,8 @@ DropboxStorage *DropboxStorage::authThroughConsole() {
if (ConfMan.hasKey("dropbox_code", "cloud")) { if (ConfMan.hasKey("dropbox_code", "cloud")) {
//phase 2: get access_token using specified code //phase 2: get access_token using specified code
return getAccessToken(ConfMan.get("dropbox_code", "cloud")); getAccessToken(ConfMan.get("dropbox_code", "cloud"));
return;
} }
debug("Navigate to this URL and press \"Allow\":"); debug("Navigate to this URL and press \"Allow\":");
@ -138,21 +139,16 @@ DropboxStorage *DropboxStorage::authThroughConsole() {
debug("Then, add dropbox_code key in [cloud] section of configuration file. You should copy the <code> value from URL and put it as value for that key.\n"); debug("Then, add dropbox_code key in [cloud] section of configuration file. You should copy the <code> value from URL and put it as value for that key.\n");
debug("Navigate to this URL to get more information on ScummVM's configuration files:"); debug("Navigate to this URL to get more information on ScummVM's configuration files:");
debug("http://wiki.scummvm.org/index.php/User_Manual/Configuring_ScummVM#Using_the_configuration_file_to_configure_ScummVM\n"); debug("http://wiki.scummvm.org/index.php/User_Manual/Configuring_ScummVM#Using_the_configuration_file_to_configure_ScummVM\n");
return 0;
} }
DropboxStorage *DropboxStorage::getAccessToken(Common::String code) { void DropboxStorage::getAccessToken(Common::String code) {
Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(saveAccessTokenCallback, "https://api.dropboxapi.com/1/oauth2/token"); Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(saveAccessTokenCallback, "https://api.dropboxapi.com/1/oauth2/token");
request->addPostField("code=" + code); request->addPostField("code=" + code);
request->addPostField("grant_type=authorization_code"); request->addPostField("grant_type=authorization_code");
request->addPostField("client_id=" + KEY); request->addPostField("client_id=" + KEY);
request->addPostField("client_secret=" + SECRET); request->addPostField("client_secret=" + SECRET);
request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F"); request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
ConnMan.addRequest(request);
//OK, that's not how I imagined that...
DropboxStorage *storage = new DropboxStorage("", "");
storage->addRequest(request);
return storage;
} }
} //end of namespace Dropbox } //end of namespace Dropbox

View file

@ -24,7 +24,7 @@
#define BACKENDS_CLOUD_DROPBOX_STORAGE_H #define BACKENDS_CLOUD_DROPBOX_STORAGE_H
#include "backends/cloud/storage.h" #include "backends/cloud/storage.h"
#include "../manager.h" #include "backends/cloud/manager.h"
namespace Cloud { namespace Cloud {
namespace Dropbox { namespace Dropbox {
@ -37,7 +37,7 @@ class DropboxStorage: public Cloud::Storage {
/** This private constructor is called from loadFromConfig(). */ /** This private constructor is called from loadFromConfig(). */
DropboxStorage(Common::String token, Common::String uid); DropboxStorage(Common::String token, Common::String uid);
static DropboxStorage *getAccessToken(Common::String code); static void getAccessToken(Common::String code);
public: public:
virtual ~DropboxStorage(); virtual ~DropboxStorage();
@ -58,10 +58,8 @@ public:
/** /**
* Show message with Dropbox auth instructions. (Temporary) * Show message with Dropbox auth instructions. (Temporary)
* Returns temporary DropboxStorage, which does network requests
* to get access token.
*/ */
static DropboxStorage *authThroughConsole(); static void authThroughConsole();
}; };
} //end of namespace Dropbox } //end of namespace Dropbox

View file

@ -1,73 +0,0 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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
* of the License, 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "backends/cloud/storage.h"
#include "common/debug.h"
#include "common/system.h"
#include "common/timer.h"
namespace Cloud {
void cloudThread(void *thread) {
Storage *cloudThread = (Storage *)thread;
cloudThread->handler();
}
Storage::Storage() : _timerStarted(false) {}
void Storage::addRequest(Request *request) {
_requests.push_back(request);
if (!_timerStarted) startTimer();
}
void Storage::handler() {
//TODO: lock mutex here (in case another handler() would be called before this one ends)
debug("\nhandler's here");
for (Common::Array<Request *>::iterator i = _requests.begin(); i != _requests.end();) {
if ((*i)->handle(_connectionManager)) {
delete (*i);
_requests.erase(i);
}
else ++i;
}
if (_requests.empty()) stopTimer();
_connectionManager.handle();
//TODO: unlock mutex here
}
void Storage::startTimer(int interval) {
Common::TimerManager *manager = g_system->getTimerManager();
if (manager->installTimerProc(cloudThread, interval, this, "Cloud Thread")) {
_timerStarted = true;
} else {
warning("Failed to create cloud thread");
}
}
void Storage::stopTimer() {
Common::TimerManager *manager = g_system->getTimerManager();
manager->removeTimerProc(cloudThread);
_timerStarted = false;
}
} //end of namespace Cloud

View file

@ -24,28 +24,13 @@
#define BACKENDS_CLOUD_STORAGE_H #define BACKENDS_CLOUD_STORAGE_H
#include "common/str.h" #include "common/str.h"
#include "common/array.h"
#include "backends/cloud/request.h"
#include "backends/networking/curl/connectionmanager.h"
namespace Cloud { namespace Cloud {
class Storage { class Storage {
friend void cloudThread(void *); //calls handler()
bool _timerStarted;
protected:
Common::Array<Request *> _requests;
Networking::ConnectionManager _connectionManager;
virtual void addRequest(Request *request); //starts the timer if it's not started
virtual void handler();
virtual void startTimer(int interval = 1000000); //1 second is the default interval
virtual void stopTimer();
public: public:
Storage(); Storage() {}
virtual ~Storage() {}; virtual ~Storage() {}
/** /**
* Lists given directory. * Lists given directory.

View file

@ -22,7 +22,6 @@ MODULE_OBJS := \
ifdef USE_CLOUD ifdef USE_CLOUD
MODULE_OBJS += \ MODULE_OBJS += \
cloud/manager.o \ cloud/manager.o \
cloud/storage.o \
cloud/dropbox/dropboxstorage.o cloud/dropbox/dropboxstorage.o
endif endif

View file

@ -25,11 +25,16 @@
#include "backends/networking/curl/connectionmanager.h" #include "backends/networking/curl/connectionmanager.h"
#include "backends/networking/curl/networkreadstream.h" #include "backends/networking/curl/networkreadstream.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/system.h"
#include "common/timer.h"
#include <curl/curl.h> #include <curl/curl.h>
using Common::Singleton;
DECLARE_SINGLETON(Networking::ConnectionManager);
namespace Networking { namespace Networking {
ConnectionManager::ConnectionManager(): _multi(0) { ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false) {
curl_global_init(CURL_GLOBAL_ALL); curl_global_init(CURL_GLOBAL_ALL);
_multi = curl_multi_init(); _multi = curl_multi_init();
} }
@ -39,13 +44,57 @@ ConnectionManager::~ConnectionManager() {
curl_global_cleanup(); curl_global_cleanup();
} }
NetworkReadStream *ConnectionManager::makeRequest(const char *url, curl_slist *headersList, Common::String postFields) { void ConnectionManager::registerEasyHandle(CURL *easy) {
NetworkReadStream *stream = new NetworkReadStream(url, headersList, postFields); curl_multi_add_handle(_multi, easy);
curl_multi_add_handle(_multi, stream->getEasyHandle()); }
return stream;
void ConnectionManager::addRequest(Request *request) {
_requests.push_back(request);
if (!_timerStarted) startTimer();
}
//private goes here:
void connectionsThread(void *ignored) {
ConnMan.handle();
}
void ConnectionManager::startTimer(int interval) {
Common::TimerManager *manager = g_system->getTimerManager();
if (manager->installTimerProc(connectionsThread, interval, 0, "Networking::ConnectionManager's Timer")) {
_timerStarted = true;
} else {
warning("Failed to install Networking::ConnectionManager's timer");
}
}
void ConnectionManager::stopTimer() {
Common::TimerManager *manager = g_system->getTimerManager();
manager->removeTimerProc(connectionsThread);
_timerStarted = false;
} }
void ConnectionManager::handle() { void ConnectionManager::handle() {
//TODO: lock mutex here (in case another handle() would be called before this one ends)
interateRequests();
processTransfers();
//TODO: unlock mutex here
}
void ConnectionManager::interateRequests() {
//call handle() of all running requests (so they can do their work)
debug("handler's here");
for (Common::Array<Request *>::iterator i = _requests.begin(); i != _requests.end();) {
if ((*i)->handle()) {
delete (*i);
_requests.erase(i);
} else ++i;
}
if (_requests.empty()) stopTimer();
}
void ConnectionManager::processTransfers() {
//check libcurl's transfers and notify requests of messages from queue (transfer completion or failure)
int transfersRunning; int transfersRunning;
curl_multi_perform(_multi, &transfersRunning); curl_multi_perform(_multi, &transfersRunning);

View file

@ -23,8 +23,12 @@
#ifndef BACKENDS_NETWORKING_CURL_CONNECTIONMANAGER_H #ifndef BACKENDS_NETWORKING_CURL_CONNECTIONMANAGER_H
#define BACKENDS_NETWORKING_CURL_CONNECTIONMANAGER_H #define BACKENDS_NETWORKING_CURL_CONNECTIONMANAGER_H
#include "backends/networking/curl/request.h"
#include "common/str.h" #include "common/str.h"
#include "common/singleton.h"
#include "common/array.h"
typedef void CURL;
typedef void CURLM; typedef void CURLM;
struct curl_slist; struct curl_slist;
@ -32,17 +36,43 @@ namespace Networking {
class NetworkReadStream; class NetworkReadStream;
class ConnectionManager { class ConnectionManager : public Common::Singleton<ConnectionManager> {
friend void connectionsThread(void *); //calls handle()
CURLM *_multi; CURLM *_multi;
bool _timerStarted;
Common::Array<Request *> _requests;
void startTimer(int interval = 1000000); //1 second is the default interval
void stopTimer();
void handle();
void interateRequests();
void processTransfers();
public: public:
ConnectionManager(); ConnectionManager();
virtual ~ConnectionManager(); virtual ~ConnectionManager();
NetworkReadStream *makeRequest(const char *url, curl_slist *headersList, Common::String postFields); /**
void handle(); * All libcurl transfers are going through this ConnectionManager.
* So, if you want to start any libcurl transfer, you must create
* an easy handle and register it using this method.
*/
void registerEasyHandle(CURL *easy);
/**
* Use this method to add new Request into manager's queue.
* Manager will periodically call handle() method of these
* Requests until they return true.
*
* @note This method starts the timer if it's not started yet.
*/
void addRequest(Request *request);
}; };
} //end of namespace Cloud /** Shortcut for accessing the connection manager. */
#define ConnMan Networking::ConnectionManager::instance()
} //end of namespace Networking
#endif #endif

View file

@ -23,6 +23,7 @@
#define FORBIDDEN_SYMBOL_ALLOW_ALL #define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "backends/networking/curl/curljsonrequest.h" #include "backends/networking/curl/curljsonrequest.h"
#include "backends/networking/curl/connectionmanager.h"
#include "backends/networking/curl/networkreadstream.h" #include "backends/networking/curl/networkreadstream.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/json.h" #include "common/json.h"
@ -56,8 +57,8 @@ char *CurlJsonRequest::getPreparedContents() {
return (char *)result; return (char *)result;
} }
bool CurlJsonRequest::handle(ConnectionManager &manager) { bool CurlJsonRequest::handle() {
if (!_stream) _stream = manager.makeRequest(_url, _headersList, _postFields); if (!_stream) _stream = new NetworkReadStream(_url, _headersList, _postFields);
if (_stream) { if (_stream) {
const int kBufSize = 16*1024; const int kBufSize = 16*1024;

View file

@ -23,7 +23,7 @@
#ifndef BACKENDS_NETWORKING_CURL_CURLJSONREQUEST_H #ifndef BACKENDS_NETWORKING_CURL_CURLJSONREQUEST_H
#define BACKENDS_NETWORKING_CURL_CURLJSONREQUEST_H #define BACKENDS_NETWORKING_CURL_CURLJSONREQUEST_H
#include "backends/cloud/request.h" #include "backends/networking/curl/request.h"
#include "common/memstream.h" #include "common/memstream.h"
struct curl_slist; struct curl_slist;
@ -32,7 +32,7 @@ namespace Networking {
class NetworkReadStream; class NetworkReadStream;
class CurlJsonRequest : public Cloud::Request { class CurlJsonRequest : public Request {
const char *_url; const char *_url;
NetworkReadStream *_stream; NetworkReadStream *_stream;
curl_slist *_headersList; curl_slist *_headersList;
@ -46,7 +46,7 @@ public:
CurlJsonRequest(Callback cb, const char *url); CurlJsonRequest(Callback cb, const char *url);
virtual ~CurlJsonRequest(); virtual ~CurlJsonRequest();
virtual bool handle(ConnectionManager &manager); virtual bool handle();
void addHeader(Common::String header); void addHeader(Common::String header);

View file

@ -23,6 +23,7 @@
#define FORBIDDEN_SYMBOL_ALLOW_ALL #define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "backends/networking/curl/networkreadstream.h" #include "backends/networking/curl/networkreadstream.h"
#include "backends/networking/curl/connectionmanager.h"
#include "common/debug.h" #include "common/debug.h"
#include <curl/curl.h> #include <curl/curl.h>
@ -47,6 +48,7 @@ NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, C
curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList); curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, postFields.size()); curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, postFields.size());
curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, postFields.c_str()); curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, postFields.c_str());
ConnMan.registerEasyHandle(_easy);
} }
NetworkReadStream::~NetworkReadStream() { NetworkReadStream::~NetworkReadStream() {

View file

@ -40,8 +40,6 @@ public:
NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields); NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields);
virtual ~NetworkReadStream(); virtual ~NetworkReadStream();
CURL *getEasyHandle() const { return _easy; }
/** /**
* Returns true if a read failed because the stream end has been reached. * Returns true if a read failed because the stream end has been reached.
* This flag is cleared by clearErr(). * This flag is cleared by clearErr().

View file

@ -20,21 +20,20 @@
* *
*/ */
#ifndef BACKENDS_CLOUD_REQUEST_H #ifndef BACKENDS_NETWORKING_CURL_REQUEST_H
#define BACKENDS_CLOUD_REQUEST_H #define BACKENDS_NETWORKING_CURL_REQUEST_H
#include "backends/networking/curl/connectionmanager.h" namespace Networking {
namespace Cloud {
class Request { class Request {
protected: protected:
typedef void(*Callback)(void *result);
/** /**
* Callback, which should be called before Request returns true in handle(). * Callback, which should be called before Request returns true in handle().
* That's the way Requests pass the result to the code which asked to create this request. * That's the way Requests pass the result to the code which asked to create this request.
*/ */
typedef void(*Callback)(void *result);
Callback _callback; Callback _callback;
public: public:
@ -47,7 +46,7 @@ public:
* @return true if request's work is complete and it may be removed from Storage's list * @return true if request's work is complete and it may be removed from Storage's list
*/ */
virtual bool handle(Networking::ConnectionManager &manager) = 0; virtual bool handle() = 0;
}; };
} //end of namespace Cloud } //end of namespace Cloud