diff options
author | Alexander Tkachev | 2016-05-27 15:21:06 +0600 |
---|---|---|
committer | Alexander Tkachev | 2016-08-24 16:07:55 +0600 |
commit | 98150beb38f73b56c7bc76f95dcc1d72290e4ac7 (patch) | |
tree | c56f8d97d30fb15f41f07f402c80e83a13cc03fc /backends | |
parent | 83b349a033d71e92e292d1f1da0578d557ec6411 (diff) | |
download | scummvm-rg350-98150beb38f73b56c7bc76f95dcc1d72290e4ac7.tar.gz scummvm-rg350-98150beb38f73b56c7bc76f95dcc1d72290e4ac7.tar.bz2 scummvm-rg350-98150beb38f73b56c7bc76f95dcc1d72290e4ac7.zip |
CLOUD: Refactor ConnectionManager/Requests system
ConnectionManager now storages Request * (not generates ids for it),
Requests have control on their RequestState, RequestIdPair is now called
Response and storages Request * with some response together.
All related classes are changed to use it in more clean and
understandable way.
Request, RequestState and Response are carefully commented/documented.
Diffstat (limited to 'backends')
19 files changed, 323 insertions, 237 deletions
diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp index 661d6fd56b..c7f6f75346 100644 --- a/backends/cloud/downloadrequest.cpp +++ b/backends/cloud/downloadrequest.cpp @@ -29,14 +29,13 @@ namespace Cloud { DownloadRequest::DownloadRequest(Storage *storage, Storage::BoolCallback callback, Common::String remoteFile, Common::DumpFile *dumpFile): Request(0), _boolCallback(callback), _remoteFileStream(0), _localFile(dumpFile) { - storage->streamFile(remoteFile, new Common::Callback<DownloadRequest, Storage::RequestReadStreamPair>(this, &DownloadRequest::streamCallback)); + storage->streamFile(remoteFile, new Common::Callback<DownloadRequest, Networking::NetworkReadStreamResponse>(this, &DownloadRequest::streamCallback)); } -void DownloadRequest::streamCallback(Storage::RequestReadStreamPair pair) { +void DownloadRequest::streamCallback(Networking::NetworkReadStreamResponse pair) { if (!pair.value) { warning("DownloadRequest: no ReadStream passed"); - ConnMan.getRequestInfo(_id).state = Networking::FINISHED; - if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, false)); + finish(); return; } @@ -46,15 +45,13 @@ void DownloadRequest::streamCallback(Storage::RequestReadStreamPair pair) { void DownloadRequest::handle() { if (!_localFile) { warning("DownloadRequest: no file to write"); - ConnMan.getRequestInfo(_id).state = Networking::FINISHED; - if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, false)); + finish(); return; } if (!_localFile->isOpen()) { warning("DownloadRequest: failed to open file to write"); - ConnMan.getRequestInfo(_id).state = Networking::FINISHED; - if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, false)); + finish(); return; } @@ -70,8 +67,7 @@ void DownloadRequest::handle() { if (readBytes != 0) if (_localFile->write(buf, readBytes) != readBytes) { warning("DownloadRequest: unable to write all received bytes into output file"); - ConnMan.getRequestInfo(_id).state = Networking::FINISHED; - if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, false)); + finish(); return; } @@ -81,8 +77,7 @@ void DownloadRequest::handle() { //TODO: do something about it actually } - ConnMan.getRequestInfo(_id).state = Networking::FINISHED; - if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, _remoteFileStream->httpResponseCode() == 200)); + finishBool(_remoteFileStream->httpResponseCode() == 200); _localFile->close(); //yes, I know it's closed automatically in ~DumpFile() } @@ -92,9 +87,17 @@ void DownloadRequest::restart() { //this request doesn't know anything about the _remoteFileStream it's reading //thus, it can't restart it warning("DownloadRequest: cannot be restarted"); - ConnMan.getRequestInfo(_id).state = Networking::FINISHED; - if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, false)); + finish(); //TODO: fix that } +void DownloadRequest::finish() { + finishBool(false); +} + +void DownloadRequest::finishBool(bool success) { + Request::finish(); + if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success)); +} + } //end of namespace Cloud diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h index 181536beba..4ea85760ae 100644 --- a/backends/cloud/downloadrequest.h +++ b/backends/cloud/downloadrequest.h @@ -26,7 +26,7 @@ #include "backends/networking/curl/request.h" #include "backends/networking/curl/networkreadstream.h" #include "backends/cloud/storage.h" -#include <common/file.h> +#include "common/file.h" namespace Cloud { @@ -35,13 +35,16 @@ class DownloadRequest: public Networking::Request { Networking::NetworkReadStream *_remoteFileStream; Common::DumpFile *_localFile; - void streamCallback(Storage::RequestReadStreamPair pair); + void streamCallback(Networking::NetworkReadStreamResponse pair); + + void finishBool(bool success); public: DownloadRequest(Storage *storage, Storage::BoolCallback callback, Common::String remoteFile, Common::DumpFile *dumpFile); virtual ~DownloadRequest() { delete _localFile; } virtual void handle(); virtual void restart(); + virtual void finish(); }; } //end of namespace Cloud diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp index 3158149c02..04fbf46ac6 100644 --- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp +++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp @@ -32,7 +32,7 @@ namespace Dropbox { DropboxListDirectoryRequest::DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::FileArrayCallback cb, bool recursive): Networking::Request(0), _requestedPath(path), _requestedRecursive(recursive), _filesCallback(cb), - _token(token), _complete(false), _requestId(-1) { + _token(token), _complete(false), _innerRequest(nullptr) { startupWork(); } @@ -40,7 +40,7 @@ void DropboxListDirectoryRequest::startupWork() { _files.clear(); _complete = false; - Networking::JsonCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::RequestJsonPair>(this, &DropboxListDirectoryRequest::responseCallback); + Networking::JsonCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback); Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder"); request->addHeader("Authorization: Bearer " + _token); request->addHeader("Content-Type: application/json"); @@ -54,11 +54,11 @@ void DropboxListDirectoryRequest::startupWork() { Common::JSONValue value(jsonRequestParameters); request->addPostField(Common::JSON::stringify(&value)); - _requestId = ConnMan.addRequest(request); + _innerRequest = ConnMan.addRequest(request); } -void DropboxListDirectoryRequest::responseCallback(Networking::RequestJsonPair pair) { +void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse pair) { Common::JSONValue *json = pair.value; if (json) { Common::JSONObject response = json->asObject(); @@ -89,7 +89,7 @@ void DropboxListDirectoryRequest::responseCallback(Networking::RequestJsonPair p bool hasMore = response.getVal("has_more")->asBool(); if (hasMore) { - Networking::JsonCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::RequestJsonPair>(this, &DropboxListDirectoryRequest::responseCallback); + Networking::JsonCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback); Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder/continue"); request->addHeader("Authorization: Bearer " + _token); request->addHeader("Content-Type: application/json"); @@ -113,22 +113,28 @@ void DropboxListDirectoryRequest::responseCallback(Networking::RequestJsonPair p } void DropboxListDirectoryRequest::handle() { - if (_complete) { - ConnMan.getRequestInfo(_id).state = Networking::FINISHED; - if (_filesCallback) (*_filesCallback)(Storage::RequestFileArrayPair(_id, _files)); - } + if (_complete) finishFiles(_files); } void DropboxListDirectoryRequest::restart() { - if (_requestId != -1) { - Networking::RequestInfo &info = ConnMan.getRequestInfo(_requestId); + if (_innerRequest) { //TODO: I'm really not sure some CurlRequest would handle this (it must stop corresponding CURL transfer) - info.state = Networking::FINISHED; //may be CANCELED or INTERRUPTED or something? - _requestId = -1; + _innerRequest->finish(); //may be CANCELED or INTERRUPTED or something? + _innerRequest = nullptr; } startupWork(); } +void DropboxListDirectoryRequest::finish() { + Common::Array<StorageFile> files; + finishFiles(files); +} + +void DropboxListDirectoryRequest::finishFiles(Common::Array<StorageFile> &files) { + Request::finish(); + if (_filesCallback) (*_filesCallback)(Storage::FileArrayResponse(this, files)); +} + } //end of namespace Dropbox } //end of namespace Cloud diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h index 9a82ef7091..e2a9ebf3a4 100644 --- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h +++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h @@ -39,17 +39,20 @@ class DropboxListDirectoryRequest: public Networking::Request { Common::String _token; bool _complete; Common::Array<StorageFile> _files; - int32 _requestId; + Request *_innerRequest; - void responseCallback(Networking::RequestJsonPair pair); + void responseCallback(Networking::JsonResponse pair); void startupWork(); + void finishFiles(Common::Array<StorageFile> &files); + public: DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::FileArrayCallback cb, bool recursive = false); virtual ~DropboxListDirectoryRequest() { delete _filesCallback; } virtual void handle(); virtual void restart(); + virtual void finish(); }; } //end of namespace Dropbox diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index b5292c83c7..a1a97e00dd 100644 --- a/backends/cloud/dropbox/dropboxstorage.cpp +++ b/backends/cloud/dropbox/dropboxstorage.cpp @@ -38,7 +38,7 @@ namespace Dropbox { Common::String DropboxStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth Common::String DropboxStorage::SECRET; //TODO: hide these secrets somehow -static void saveAccessTokenCallback(Networking::RequestJsonPair pair) { +static void saveAccessTokenCallback(Networking::JsonResponse pair) { Common::JSONValue *json = (Common::JSONValue *)pair.value; if (json) { debug("saveAccessTokenCallback:"); @@ -55,6 +55,7 @@ static void saveAccessTokenCallback(Networking::RequestJsonPair pair) { ConfMan.set("storage1_access_token", result.getVal("access_token")->asString(), "cloud"); ConfMan.set("storage1_user_id", result.getVal("uid")->asString(), "cloud"); ConfMan.removeKey("dropbox_code", "cloud"); + ConfMan.flushToDisk(); debug("Now please restart ScummVM to apply the changes."); } @@ -84,11 +85,11 @@ void DropboxStorage::printFiles(Common::Array<StorageFile> files) { debug("\t%s", files[i].name().c_str()); } -int32 DropboxStorage::listDirectory(Common::String path, FileArrayCallback outerCallback, bool recursive) { +Networking::Request *DropboxStorage::listDirectory(Common::String path, FileArrayCallback outerCallback, bool recursive) { return ConnMan.addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, recursive)); } -int32 DropboxStorage::streamFile(Common::String path, ReadStreamCallback callback) { +Networking::Request *DropboxStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback callback) { Common::JSONObject jsonRequestParameters; jsonRequestParameters.setVal("path", new Common::JSONValue(path)); Common::JSONValue value(jsonRequestParameters); @@ -98,32 +99,32 @@ int32 DropboxStorage::streamFile(Common::String path, ReadStreamCallback callbac request->addHeader("Dropbox-API-Arg: " + Common::JSON::stringify(&value)); request->addHeader("Content-Type: "); //required to be empty (as we do POST, it's usually app/form-url-encoded) - RequestReadStreamPair pair = request->execute(); + Networking::NetworkReadStreamResponse pair = request->execute(); if (callback) (*callback)(pair); - return pair.id; + return pair.request; } -int32 DropboxStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) { +Networking::Request *DropboxStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) { Common::DumpFile *f = new Common::DumpFile(); if (!f->open(localPath, true)) { warning("DropboxStorage: unable to open file to download into"); - if (callback) (*callback)(RequestBoolPair(-1, false)); + if (callback) (*callback)(BoolResponse(nullptr, false)); delete f; - return -1; + return nullptr; } return ConnMan.addRequest(new DownloadRequest(this, callback, remotePath, f)); } -int32 DropboxStorage::syncSaves(BoolCallback callback) { +Networking::Request *DropboxStorage::syncSaves(BoolCallback callback) { //this is not the real syncSaves() implementation //"" is root in Dropbox, not "/" //this must create all these directories: return download("/remote/test.jpg", "local/a/b/c/d/test.jpg", 0); } -int32 DropboxStorage::info(StorageInfoCallback outerCallback) { - Networking::JsonCallback innerCallback = new Common::CallbackBridge<DropboxStorage, RequestStorageInfoPair, Networking::RequestJsonPair>(this, &DropboxStorage::infoInnerCallback, outerCallback); +Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback) { + Networking::JsonCallback innerCallback = new Common::CallbackBridge<DropboxStorage, StorageInfoResponse, Networking::JsonResponse>(this, &DropboxStorage::infoInnerCallback, outerCallback); Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/1/account/info"); request->addHeader("Authorization: Bearer " + _token); return ConnMan.addRequest(request); @@ -133,7 +134,7 @@ int32 DropboxStorage::info(StorageInfoCallback outerCallback) { //and then calls the outerCallback (which wants to receive StorageInfo, not void *) } -void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::RequestJsonPair pair) { +void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse pair) { Common::JSONValue *json = pair.value; if (!json) { warning("NULL passed instead of JSON"); @@ -151,14 +152,14 @@ void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networ uint32 quotaNormal = quota.getVal("normal")->asNumber(); uint32 quotaShared = quota.getVal("shared")->asNumber(); uint32 quotaAllocated = quota.getVal("quota")->asNumber(); - (*outerCallback)(RequestStorageInfoPair(-1, StorageInfo(uid, name, email, quotaNormal+quotaShared, quotaAllocated))); + (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaNormal+quotaShared, quotaAllocated))); delete outerCallback; } delete json; } -void DropboxStorage::infoMethodCallback(RequestStorageInfoPair pair) { +void DropboxStorage::infoMethodCallback(StorageInfoResponse pair) { debug("\nStorage info:"); debug("User name: %s", pair.value.name().c_str()); debug("Email: %s", pair.value.email().c_str()); @@ -216,7 +217,7 @@ void DropboxStorage::authThroughConsole() { } void DropboxStorage::getAccessToken(Common::String code) { - Networking::JsonCallback callback = new Common::GlobalFunctionCallback<Networking::RequestJsonPair>(saveAccessTokenCallback); + Networking::JsonCallback callback = new Common::GlobalFunctionCallback<Networking::JsonResponse>(saveAccessTokenCallback); Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, "https://api.dropboxapi.com/1/oauth2/token"); request->addPostField("code=" + code); request->addPostField("grant_type=authorization_code"); diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h index dd082b25f1..f95d0c9812 100644 --- a/backends/cloud/dropbox/dropboxstorage.h +++ b/backends/cloud/dropbox/dropboxstorage.h @@ -41,7 +41,7 @@ class DropboxStorage: public Cloud::Storage { static void getAccessToken(Common::String code); /** Constructs StorageInfo based on JSON response from cloud. */ - void infoInnerCallback(StorageInfoCallback outerCallback, Networking::RequestJsonPair json); + void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json); void printFiles(Common::Array<StorageFile> files); @@ -65,34 +65,34 @@ public: /** Public Cloud API comes down there. */ /** Returns Common::Array<StorageFile>. */ - virtual int32 listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false); + virtual Networking::Request *listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false); /** Calls the callback when finished. */ - virtual int32 upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) { return -1; } //TODO + virtual Networking::Request *upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) { return nullptr; } //TODO /** Returns pointer to Networking::NetworkReadStream. */ - virtual int32 streamFile(Common::String path, ReadStreamCallback callback); + virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback); /** Calls the callback when finished. */ - virtual int32 download(Common::String remotePath, Common::String localPath, BoolCallback callback); + virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback); /** Calls the callback when finished. */ - virtual int32 remove(Common::String path, BoolCallback callback) { return -1; } //TODO + virtual Networking::Request *remove(Common::String path, BoolCallback callback) { return nullptr; } //TODO /** Calls the callback when finished. */ - virtual int32 syncSaves(BoolCallback callback); + virtual Networking::Request *syncSaves(BoolCallback callback); /** Calls the callback when finished. */ - virtual int32 createDirectory(Common::String path, BoolCallback callback) { return -1; } //TODO + virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback) { return nullptr; } //TODO /** Calls the callback when finished. */ - virtual int32 touch(Common::String path, BoolCallback callback) { return -1; } //TODO + virtual Networking::Request *touch(Common::String path, BoolCallback callback) { return nullptr; } //TODO /** Returns the StorageInfo struct. */ - virtual int32 info(StorageInfoCallback callback); + virtual Networking::Request *info(StorageInfoCallback callback); /** This method is passed into info(). (Temporary) */ - void infoMethodCallback(RequestStorageInfoPair pair); + void infoMethodCallback(StorageInfoResponse pair); /** Returns whether saves sync process is running. */ virtual bool isSyncing() { return false; } //TODO diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp index 237557df59..e181fecabb 100644 --- a/backends/cloud/onedrive/onedrivestorage.cpp +++ b/backends/cloud/onedrive/onedrivestorage.cpp @@ -44,7 +44,7 @@ OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String user _token(accessToken), _uid(userId), _refreshToken(refreshToken) {} OneDriveStorage::OneDriveStorage(Common::String code) { - getAccessToken(new Common::Callback<OneDriveStorage, RequestBoolPair>(this, &OneDriveStorage::codeFlowComplete), code); + getAccessToken(new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::codeFlowComplete), code); } OneDriveStorage::~OneDriveStorage() {} @@ -54,11 +54,11 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code) if (!codeFlow && _refreshToken == "") { warning("OneDriveStorage: no refresh token available to get new access token."); - if (callback) (*callback)(RequestBoolPair(-1, false)); + if (callback) (*callback)(BoolResponse(nullptr, false)); return; } - Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, RequestBoolPair, Networking::RequestJsonPair>(this, &OneDriveStorage::tokenRefreshed, callback); + Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, BoolResponse, Networking::JsonResponse>(this, &OneDriveStorage::tokenRefreshed, callback); Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://login.live.com/oauth20_token.srf"); if (codeFlow) { request->addPostField("code=" + code); @@ -73,11 +73,11 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code) ConnMan.addRequest(request); } -void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::RequestJsonPair pair) { +void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse pair) { Common::JSONValue *json = pair.value; if (!json) { warning("OneDriveStorage: got NULL instead of JSON"); - if (callback) (*callback)(RequestBoolPair(-1, false)); + if (callback) (*callback)(BoolResponse(nullptr, false)); return; } @@ -85,18 +85,18 @@ void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::RequestJ if (!result.contains("access_token") || !result.contains("user_id") || !result.contains("refresh_token")) { warning("Bad response, no token or user_id passed"); debug("%s", json->stringify().c_str()); - if (callback) (*callback)(RequestBoolPair(-1, false)); + if (callback) (*callback)(BoolResponse(nullptr, false)); } else { _token = result.getVal("access_token")->asString(); _uid = result.getVal("user_id")->asString(); _refreshToken = result.getVal("refresh_token")->asString(); g_system->getCloudManager()->save(); //ask CloudManager to save our new refreshToken - if (callback) (*callback)(RequestBoolPair(-1, true)); + if (callback) (*callback)(BoolResponse(nullptr, true)); } delete json; } -void OneDriveStorage::codeFlowComplete(RequestBoolPair pair) { +void OneDriveStorage::codeFlowComplete(BoolResponse pair) { if (!pair.value) { warning("OneDriveStorage: failed to get access token through code flow"); return; @@ -115,7 +115,7 @@ void OneDriveStorage::saveConfig(Common::String keyPrefix) { ConfMan.set(keyPrefix + "refresh_token", _refreshToken, "cloud"); } -void OneDriveStorage::printJson(Networking::RequestJsonPair pair) { +void OneDriveStorage::printJson(Networking::JsonResponse pair) { Common::JSONValue *json = pair.value; if (!json) { warning("printJson: NULL"); @@ -126,10 +126,10 @@ void OneDriveStorage::printJson(Networking::RequestJsonPair pair) { delete json; } -void OneDriveStorage::fileInfoCallback(ReadStreamCallback outerCallback, Networking::RequestJsonPair pair) { +void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse pair) { if (!pair.value) { warning("fileInfoCallback: NULL"); - if (outerCallback) (*outerCallback)(RequestReadStreamPair(pair.id, 0)); + if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(pair.request, 0)); return; } @@ -137,44 +137,44 @@ void OneDriveStorage::fileInfoCallback(ReadStreamCallback outerCallback, Network if (result.contains("@content.downloadUrl")) { const char *url = result.getVal("@content.downloadUrl")->asString().c_str(); if (outerCallback) - (*outerCallback)(RequestReadStreamPair( - pair.id, + (*outerCallback)(Networking::NetworkReadStreamResponse( + pair.request, new Networking::NetworkReadStream(url, 0, "") )); } else { warning("downloadUrl not found in passed JSON"); debug("%s", pair.value->stringify().c_str()); - if (outerCallback) (*outerCallback)(RequestReadStreamPair(pair.id, 0)); + if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(pair.request, 0)); } delete pair.value; } -int32 OneDriveStorage::streamFile(Common::String path, ReadStreamCallback outerCallback) { - Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + path + ":/"; - Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, RequestReadStreamPair, Networking::RequestJsonPair>(this, &OneDriveStorage::fileInfoCallback, outerCallback); +Networking::Request *OneDriveStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback outerCallback) { + Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + path; + Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, Networking::NetworkReadStreamResponse, Networking::JsonResponse>(this, &OneDriveStorage::fileInfoCallback, outerCallback); Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, url.c_str()); request->addHeader("Authorization: Bearer " + _token); return ConnMan.addRequest(request); } -int32 OneDriveStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) { +Networking::Request *OneDriveStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) { Common::DumpFile *f = new Common::DumpFile(); if (!f->open(localPath, true)) { warning("OneDriveStorage: unable to open file to download into"); - if (callback) (*callback)(RequestBoolPair(-1, false)); + if (callback) (*callback)(BoolResponse(nullptr, false)); delete f; - return -1; + return nullptr; } return ConnMan.addRequest(new DownloadRequest(this, callback, remotePath, f)); } -void OneDriveStorage::fileDownloaded(RequestBoolPair pair) { +void OneDriveStorage::fileDownloaded(BoolResponse pair) { if (pair.value) debug("file downloaded!"); else debug("download failed!"); } -int32 OneDriveStorage::syncSaves(BoolCallback callback) { +Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback) { //this is not the real syncSaves() implementation /* Networking::JsonCallback innerCallback = new Common::Callback<OneDriveStorage, Networking::RequestJsonPair>(this, &OneDriveStorage::printJson); @@ -182,7 +182,7 @@ int32 OneDriveStorage::syncSaves(BoolCallback callback) { request->addHeader("Authorization: bearer " + _token); return ConnMan.addRequest(request); */ - return download("pic.jpg", "local/onedrive/2/doom.jpg", new Common::Callback<OneDriveStorage, RequestBoolPair>(this, &OneDriveStorage::fileDownloaded)); + return download("pic.jpg", "local/onedrive/2/doom.jpg", new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::fileDownloaded)); } OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) { diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h index be2bcdf04c..858af6c9bf 100644 --- a/backends/cloud/onedrive/onedrivestorage.h +++ b/backends/cloud/onedrive/onedrivestorage.h @@ -44,13 +44,13 @@ class OneDriveStorage: public Cloud::Storage { */ OneDriveStorage(Common::String code); - void tokenRefreshed(BoolCallback callback, Networking::RequestJsonPair pair); - void codeFlowComplete(RequestBoolPair pair); + void tokenRefreshed(BoolCallback callback, Networking::JsonResponse pair); + void codeFlowComplete(BoolResponse pair); - void printJson(Networking::RequestJsonPair pair); - void fileDownloaded(RequestBoolPair pair); + void printJson(Networking::JsonResponse pair); + void fileDownloaded(BoolResponse pair); - void fileInfoCallback(ReadStreamCallback outerCallback, Networking::RequestJsonPair pair); + void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse pair); public: virtual ~OneDriveStorage(); @@ -71,31 +71,31 @@ public: /** Public Cloud API comes down there. */ /** Returns Common::Array<StorageFile>. */ - virtual int32 listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) { return -1; } //TODO + virtual Networking::Request *listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) { return nullptr; } //TODO /** Calls the callback when finished. */ - virtual int32 upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) { return -1; } //TODO + virtual Networking::Request *upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) { return nullptr; } //TODO /** Returns pointer to Networking::NetworkReadStream. */ - virtual int32 streamFile(Common::String path, ReadStreamCallback callback); + virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback); /** Calls the callback when finished. */ - virtual int32 download(Common::String remotePath, Common::String localPath, BoolCallback callback); + virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback); /** Calls the callback when finished. */ - virtual int32 remove(Common::String path, BoolCallback callback) { return -1; } //TODO + virtual Networking::Request *remove(Common::String path, BoolCallback callback) { return nullptr; } //TODO /** Calls the callback when finished. */ - virtual int32 syncSaves(BoolCallback callback); + virtual Networking::Request *syncSaves(BoolCallback callback); /** Calls the callback when finished. */ - virtual int32 createDirectory(Common::String path, BoolCallback callback) { return -1; } //TODO + virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback) { return nullptr; } //TODO /** Calls the callback when finished. */ - virtual int32 touch(Common::String path, BoolCallback callback) { return -1; } //TODO + virtual Networking::Request *touch(Common::String path, BoolCallback callback) { return nullptr; } //TODO /** Returns the StorageInfo struct. */ - virtual int32 info(StorageInfoCallback callback) { return -1; } //TODO + virtual Networking::Request *info(StorageInfoCallback callback) { return nullptr; } //TODO /** Returns whether saves sync process is running. */ virtual bool isSyncing() { return false; } //TODO diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp index d3ec0cc668..6943d89297 100644 --- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp +++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp @@ -38,50 +38,45 @@ OneDriveTokenRefresher::OneDriveTokenRefresher(OneDriveStorage *parent, Networki _parentStorage(parent), _innerRequest( new CurlJsonRequest( - new Common::Callback<OneDriveTokenRefresher, Networking::RequestJsonPair>(this, &OneDriveTokenRefresher::innerRequestCallback), + new Common::Callback<OneDriveTokenRefresher, Networking::JsonResponse>(this, &OneDriveTokenRefresher::innerRequestCallback), url ) - ), _jsonCallback(callback), _retryId(-1), _started(false) {} + ), _jsonCallback(callback), _retryRequest(nullptr), _started(false) {} OneDriveTokenRefresher::~OneDriveTokenRefresher() {} -void OneDriveTokenRefresher::innerRequestCallback(Networking::RequestJsonPair pair) { +void OneDriveTokenRefresher::innerRequestCallback(Networking::JsonResponse pair) { if (!pair.value) { //notify user of failure warning("OneDriveTokenRefresher: got NULL instead of JSON"); - ConnMan.getRequestInfo(_id).state = Networking::FINISHED; - if (_jsonCallback) (*_jsonCallback)(Networking::RequestJsonPair(_id, 0)); + finish(); return; } Common::JSONObject result = pair.value->asObject(); if (result.contains("error")) { //new token needed => request token & then retry original request - ConnMan.getRequestInfo(pair.id).state = Networking::PAUSED; - _retryId = pair.id; + if (pair.request) pair.request->pause(); + _retryRequest = pair.request; delete pair.value; - _parentStorage->getAccessToken(new Common::Callback<OneDriveTokenRefresher, Storage::RequestBoolPair>(this, &OneDriveTokenRefresher::tokenRefreshed)); + _parentStorage->getAccessToken(new Common::Callback<OneDriveTokenRefresher, Storage::BoolResponse>(this, &OneDriveTokenRefresher::tokenRefreshed)); return; } //notify user of success - ConnMan.getRequestInfo(_id).state = Networking::FINISHED; - if (_jsonCallback) (*_jsonCallback)(Networking::RequestJsonPair(_id, pair.value)); + finishJson(pair.value); } -void OneDriveTokenRefresher::tokenRefreshed(Storage::RequestBoolPair pair) { +void OneDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse pair) { if (!pair.value) { //failed to refresh token, notify user with NULL in original callback warning("OneDriveTokenRefresher: failed to refresh token"); - ConnMan.getRequestInfo(_id).state = Networking::FINISHED; - if (_jsonCallback) (*_jsonCallback)(Networking::RequestJsonPair(_id, 0)); + finish(); return; } //successfully received refreshed token, can restart the original request now - Networking::RequestInfo &info = ConnMan.getRequestInfo(_retryId); - info.state = Networking::RETRY; - info.retryInSeconds = 1; + if (_retryRequest) _retryRequest->retry(1); //update headers: first change header with token, then pass those to request for (uint32 i = 0; i < _headers.size(); ++i) { @@ -89,7 +84,7 @@ void OneDriveTokenRefresher::tokenRefreshed(Storage::RequestBoolPair pair) { _headers[i] = "Authorization: bearer " + _parentStorage->accessToken(); } } - CurlJsonRequest *retryRequest = (CurlJsonRequest *)info.request; + CurlJsonRequest *retryRequest = (CurlJsonRequest *)_retryRequest; if (retryRequest) retryRequest->setHeaders(_headers); } @@ -105,11 +100,19 @@ void OneDriveTokenRefresher::handle() { void OneDriveTokenRefresher::restart() { //can't restart as all headers were passed to _innerRequest which is probably dead now warning("OneDriveTokenRefresher: cannot be restarted"); - ConnMan.getRequestInfo(_id).state = Networking::FINISHED; - if (_jsonCallback) (*_jsonCallback)(Networking::RequestJsonPair(_id, 0)); + finish(); } -Cloud::Storage::RequestReadStreamPair OneDriveTokenRefresher::execute() { +void OneDriveTokenRefresher::finish() { + finishJson(0); +} + +void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) { + Request::finish(); + if (_jsonCallback) (*_jsonCallback)(Networking::JsonResponse(this, json)); +} + +Networking::NetworkReadStreamResponse OneDriveTokenRefresher::execute() { if (!_started) { for (uint32 i = 0; i < _headers.size(); ++i) _innerRequest->addHeader(_headers[i]); diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.h b/backends/cloud/onedrive/onedrivetokenrefresher.h index 58b7dcedb4..c09879feed 100644 --- a/backends/cloud/onedrive/onedrivetokenrefresher.h +++ b/backends/cloud/onedrive/onedrivetokenrefresher.h @@ -35,24 +35,27 @@ class OneDriveStorage; class OneDriveTokenRefresher: public Networking::CurlJsonRequest { OneDriveStorage *_parentStorage; Common::Array<Common::String> _headers; - Networking::CurlJsonRequest *_innerRequest; + CurlJsonRequest *_innerRequest; Networking::JsonCallback _jsonCallback; - int32 _retryId; + Request *_retryRequest; bool _started; - void innerRequestCallback(Networking::RequestJsonPair pair); - void tokenRefreshed(Storage::RequestBoolPair pair); + void innerRequestCallback(Networking::JsonResponse pair); + void tokenRefreshed(Storage::BoolResponse pair); + + virtual void finishJson(Common::JSONValue *json); public: OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, const char *url); virtual ~OneDriveTokenRefresher(); virtual void handle(); virtual void restart(); + virtual void finish(); virtual void setHeaders(Common::Array<Common::String> &headers) { _headers = headers; } virtual void addHeader(Common::String header) { _headers.push_back(header); } virtual void addPostField(Common::String field) { _innerRequest->addPostField(field); } - virtual Cloud::Storage::RequestReadStreamPair execute(); + virtual Networking::NetworkReadStreamResponse execute(); }; } //end of namespace OneDrive diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h index 0114b46dee..8ae5308a1c 100644 --- a/backends/cloud/storage.h +++ b/backends/cloud/storage.h @@ -23,28 +23,26 @@ #ifndef BACKENDS_CLOUD_STORAGE_H #define BACKENDS_CLOUD_STORAGE_H +#include "backends/cloud/storagefile.h" +#include "backends/cloud/storageinfo.h" +#include "backends/networking/curl/request.h" +#include "backends/networking/curl/curlrequest.h" #include "common/array.h" #include "common/stream.h" #include "common/str.h" #include "common/callback.h" -#include "backends/cloud/storagefile.h" -#include "backends/cloud/storageinfo.h" -#include "backends/networking/curl/networkreadstream.h" -#include <backends/networking/curl/request.h> namespace Cloud { class Storage { public: - typedef Networking::RequestIdPair<Common::Array<StorageFile>&> RequestFileArrayPair; - typedef Networking::RequestIdPair<Networking::NetworkReadStream *> RequestReadStreamPair; - typedef Networking::RequestIdPair<StorageInfo> RequestStorageInfoPair; - typedef Networking::RequestIdPair<bool> RequestBoolPair; + typedef Networking::Response<Common::Array<StorageFile>&> FileArrayResponse; + typedef Networking::Response<StorageInfo> StorageInfoResponse; + typedef Networking::Response<bool> BoolResponse; - typedef Common::BaseCallback<RequestFileArrayPair> *FileArrayCallback; - typedef Common::BaseCallback<RequestReadStreamPair> *ReadStreamCallback; - typedef Common::BaseCallback<RequestStorageInfoPair> *StorageInfoCallback; - typedef Common::BaseCallback<RequestBoolPair> *BoolCallback; + typedef Common::BaseCallback<FileArrayResponse> *FileArrayCallback; + typedef Common::BaseCallback<StorageInfoResponse> *StorageInfoCallback; + typedef Common::BaseCallback<BoolResponse> *BoolCallback; Storage() {} virtual ~Storage() {} @@ -66,37 +64,37 @@ public: /** * Public Cloud API comes down there. * - * All Cloud API methods return int32 request id, which might be used to access - * request through ConnectionManager. All methods also accept a callback, which - * would be called, when request is complete. + * All Cloud API methods return Networking::Request *, which + * might be used to control request. All methods also accept + * a callback, which is called, when request is complete. */ /** Returns Common::Array<StorageFile>. */ - virtual int32 listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) = 0; + virtual Networking::Request *listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) = 0; /** Calls the callback when finished. */ - virtual int32 upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) = 0; + virtual Networking::Request *upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) = 0; /** Returns pointer to Networking::NetworkReadStream. */ - virtual int32 streamFile(Common::String path, ReadStreamCallback callback) = 0; + virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback) = 0; /** Calls the callback when finished. */ - virtual int32 download(Common::String remotePath, Common::String localPath, BoolCallback callback) = 0; + virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback) = 0; /** Calls the callback when finished. */ - virtual int32 remove(Common::String path, BoolCallback callback) = 0; + virtual Networking::Request *remove(Common::String path, BoolCallback callback) = 0; /** Calls the callback when finished. */ - virtual int32 syncSaves(BoolCallback callback) = 0; + virtual Networking::Request *syncSaves(BoolCallback callback) = 0; /** Calls the callback when finished. */ - virtual int32 createDirectory(Common::String path, BoolCallback callback) = 0; + virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback) = 0; /** Calls the callback when finished. */ - virtual int32 touch(Common::String path, BoolCallback callback) = 0; + virtual Networking::Request *touch(Common::String path, BoolCallback callback) = 0; /** Returns the StorageInfo struct. */ - virtual int32 info(StorageInfoCallback callback) = 0; + virtual Networking::Request *info(StorageInfoCallback callback) = 0; /** Returns whether saves sync process is running. */ virtual bool isSyncing() = 0; diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp index 9d88c59b25..ef2afc2655 100644 --- a/backends/networking/curl/connectionmanager.cpp +++ b/backends/networking/curl/connectionmanager.cpp @@ -34,7 +34,7 @@ DECLARE_SINGLETON(Networking::ConnectionManager); namespace Networking { -ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false), _nextId(0) { +ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false) { curl_global_init(CURL_GLOBAL_ALL); _multi = curl_multi_init(); } @@ -48,16 +48,10 @@ void ConnectionManager::registerEasyHandle(CURL *easy) { curl_multi_add_handle(_multi, easy); } -int32 ConnectionManager::addRequest(Request *request) { - int32 newId = _nextId++; - _requests[newId] = RequestInfo(newId, request); - request->setId(newId); +Request *ConnectionManager::addRequest(Request *request) { + _requests.push_back(request); if (!_timerStarted) startTimer(); - return newId; -} - -RequestInfo &ConnectionManager::getRequestInfo(int32 id) { - return _requests[id]; + return request; } //private goes here: @@ -91,36 +85,22 @@ void ConnectionManager::handle() { void ConnectionManager::interateRequests() { //call handle() of all running requests (so they can do their work) - debug("handling %d request(s)", _requests.size()); - Common::Array<int32> idsToRemove; - for (Common::HashMap<int32, RequestInfo>::iterator i = _requests.begin(); i != _requests.end(); ++i) { - RequestInfo &info = _requests[i->_key]; + debug("handling %d request(s)", _requests.size()); + for (Common::Array<Request *>::iterator i = _requests.begin(); i != _requests.end();) { + Request *request = *i; + if (!request || request->state() == FINISHED) { + delete (*i); + _requests.erase(i); + continue; + } - switch(info.state) { - case FINISHED: - delete info.request; - info.request = 0; - idsToRemove.push_back(info.id); - break; - - case PROCESSING: - info.request->handle(); - break; - - case RETRY: - if (info.retryInSeconds > 0) --info.retryInSeconds; - else { - info.state = PROCESSING; - info.request->restart(); - debug("request restarted"); - } - - default: - ; //nothing to do + if (request) { + if (request->state() == PROCESSING) request->handle(); + else if (request->state() == RETRY) request->handleRetry(); } + + ++i; } - for (uint32 i = 0; i < idsToRemove.size(); ++i) - _requests.erase(idsToRemove[i]); if (_requests.empty()) stopTimer(); } diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h index 15327a28b2..2d37c0595c 100644 --- a/backends/networking/curl/connectionmanager.h +++ b/backends/networking/curl/connectionmanager.h @@ -36,30 +36,12 @@ namespace Networking { class NetworkReadStream; -enum RequestState { - PROCESSING, - PAUSED, - RETRY, - FINISHED -}; - -struct RequestInfo { - int32 id; - Request *request; - RequestState state; - uint32 retryInSeconds; - - RequestInfo() : id(-1), request(0), state(FINISHED), retryInSeconds(0) {} - RequestInfo(int32 rqId, Request *rq) : id(rqId), request(rq), state(PROCESSING), retryInSeconds(0) {} -}; - class ConnectionManager : public Common::Singleton<ConnectionManager> { friend void connectionsThread(void *); //calls handle() CURLM *_multi; bool _timerStarted; - Common::HashMap<int32, RequestInfo> _requests; - int32 _nextId; + Common::Array<Request *> _requests; void startTimer(int interval = 1000000); //1 second is the default interval void stopTimer(); @@ -81,15 +63,15 @@ public: /** * Use this method to add new Request into manager's queue. * Manager will periodically call handle() method of these - * Requests until they return true. + * Requests until they set their state to FINISHED. + * + * If Request's state is RETRY, handleRetry() is called instead. * * @note This method starts the timer if it's not started yet. * - * @return generated Request's id, which might be used to get its status + * @return the same Request pointer, just as a shortcut */ - int32 addRequest(Request *request); - - RequestInfo &getRequestInfo(int32 id); + Request *addRequest(Request *request); }; /** Shortcut for accessing the connection manager. */ diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp index 326d8e27a0..3c598d7f18 100644 --- a/backends/networking/curl/curljsonrequest.cpp +++ b/backends/networking/curl/curljsonrequest.cpp @@ -69,14 +69,11 @@ void CurlJsonRequest::handle() { if (_stream->httpResponseCode() != 200) warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode()); - ConnMan.getRequestInfo(_id).state = Networking::FINISHED; - if (_jsonCallback) { - char *contents = getPreparedContents(); - if (_stream->httpResponseCode() != 200) - debug("%s", contents); - Common::JSONValue *json = Common::JSON::parse(contents); - (*_jsonCallback)(RequestJsonPair(_id, json)); //potential memory leak, free it in your callbacks! - } + char *contents = getPreparedContents(); + if (_stream->httpResponseCode() != 200) + debug("%s", contents); + Common::JSONValue *json = Common::JSON::parse(contents); + finishJson(json); } } } @@ -88,4 +85,14 @@ void CurlJsonRequest::restart() { //with no stream available next handle() will create another one } +void CurlJsonRequest::finishJson(Common::JSONValue *json) { + Request::finish(); + if (_jsonCallback) (*_jsonCallback)(JsonResponse(this, json)); //potential memory leak, free it in your callbacks! + else delete json; +} + +void CurlJsonRequest::finish() { + finishJson(0); +} + } //end of namespace Networking diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h index 5e78bd1965..0a560f93f4 100644 --- a/backends/networking/curl/curljsonrequest.h +++ b/backends/networking/curl/curljsonrequest.h @@ -29,10 +29,8 @@ namespace Networking { -class NetworkReadStream; - -typedef RequestIdPair<Common::JSONValue*> RequestJsonPair; -typedef Common::BaseCallback<RequestJsonPair> *JsonCallback; +typedef Response<Common::JSONValue *> JsonResponse; +typedef Common::BaseCallback<JsonResponse> *JsonCallback; class CurlJsonRequest: public CurlRequest { JsonCallback _jsonCallback; @@ -41,12 +39,17 @@ class CurlJsonRequest: public CurlRequest { /** Prepares raw bytes from _contentsStream to be parsed with Common::JSON::parse(). */ char *getPreparedContents(); +protected: + /** Sets FINISHED state and passes the JSONValue * into user's callback in JsonResponse. */ + virtual void finishJson(Common::JSONValue *json); + public: CurlJsonRequest(JsonCallback cb, Common::String url); virtual ~CurlJsonRequest(); virtual void handle(); virtual void restart(); + virtual void finish(); }; } //end of namespace Networking diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp index f01a430b87..a8eb425412 100644 --- a/backends/networking/curl/curlrequest.cpp +++ b/backends/networking/curl/curlrequest.cpp @@ -40,10 +40,10 @@ CurlRequest::~CurlRequest() { void CurlRequest::handle() { if (!_stream) _stream = new NetworkReadStream(_url.c_str(), _headersList, _postFields); - if (_stream && _stream->eos()) { + if (_stream && _stream->eos()) { if (_stream->httpResponseCode() != 200) warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode()); - ConnMan.getRequestInfo(_id).state = Networking::FINISHED; + finish(); } } @@ -71,13 +71,13 @@ void CurlRequest::addPostField(Common::String keyValuePair) { _postFields += "&" + keyValuePair; } -Cloud::Storage::RequestReadStreamPair CurlRequest::execute() { +NetworkReadStreamResponse CurlRequest::execute() { if (!_stream) { _stream = new NetworkReadStream(_url.c_str(), _headersList, _postFields); ConnMan.addRequest(this); } - return Cloud::Storage::RequestReadStreamPair(_id, _stream); + return NetworkReadStreamResponse(this, _stream); } } //end of namespace Networking diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h index 18a41a1c06..5677720b1d 100644 --- a/backends/networking/curl/curlrequest.h +++ b/backends/networking/curl/curlrequest.h @@ -24,7 +24,6 @@ #define BACKENDS_NETWORKING_CURL_CURLREQUEST_H #include "backends/networking/curl/request.h" -#include "backends/cloud/storage.h" #include "common/str.h" #include "common/array.h" @@ -34,6 +33,9 @@ namespace Networking { class NetworkReadStream; +typedef Response<NetworkReadStream *> NetworkReadStreamResponse; +typedef Common::BaseCallback<NetworkReadStreamResponse> *NetworkReadStreamCallback; + class CurlRequest: public Request { protected: Common::String _url; @@ -48,12 +50,20 @@ public: virtual void handle(); virtual void restart(); + /** Replaces all headers with the passed array of headers. */ virtual void setHeaders(Common::Array<Common::String> &headers); + + /** Adds a header into headers list. */ virtual void addHeader(Common::String header); + + /** Adds a post field (key=value pair). */ virtual void addPostField(Common::String field); - /** Start this Request with ConnMan. Returns its ReadStream and request id. */ - virtual Cloud::Storage::RequestReadStreamPair execute(); + /** + * Starts this Request with ConnMan. + * @return its NetworkReadStream in NetworkReadStreamResponse. + */ + virtual NetworkReadStreamResponse execute(); }; } //end of namespace Networking diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h index 6431a01fee..a0c87460cb 100644 --- a/backends/networking/curl/networkreadstream.h +++ b/backends/networking/curl/networkreadstream.h @@ -83,6 +83,6 @@ public: long httpResponseCode(); }; -} //end of namespace Cloud +} //end of namespace Networking #endif diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h index d81fe903b8..ff919e02f1 100644 --- a/backends/networking/curl/request.h +++ b/backends/networking/curl/request.h @@ -28,40 +28,124 @@ namespace Networking { -template<typename T> struct RequestIdPair { - int32 id; +class Request; + +/** +* Response<T> is a struct to be returned from Request +* to user's callbacks. It's a type safe way to indicate +* which "return value" Request has and user awaits. +* +* It just keeps a Request pointer together with +* some T value (which might be a pointer, a reference +* or a plain type (copied by value)). +* +* To make it more convenient, typedefs are used. +* For example, Response<void *> is called DataResponse +* and corresponding callback pointer is DataCallback. +*/ + +template<typename T> struct Response { + Request *request; T value; - RequestIdPair(int32 rid, T v) : id(rid), value(v) {} + Response(Request *rq, T v) : request(rq), value(v) {} }; -typedef RequestIdPair<void *> RequestDataPair; -typedef Common::BaseCallback<RequestDataPair> *DataCallback; +typedef Response<void *> DataReponse; +typedef Common::BaseCallback<DataReponse> *DataCallback; + +/** +* RequestState is used to indicate current Request state. +* ConnectionManager uses it to decide what to do with the Request. +* +* PROCESSING state indicates that Request is working. +* ConnectionManager calls handle() method of Requests in that state. +* +* PAUSED state indicates that Request is not working. +* ConnectionManager keeps Requests in that state and doesn't call any methods of those. +* +* RETRY state indicates that Request must restart after a few seconds. +* ConnectionManager calls handleRetry() method of Requests in that state. +* Default handleRetry() implementation decreases _retryInSeconds value +* until it reaches zero. When it does, Request's restart() method is called. +* +* FINISHED state indicates that Request did the work and might be deleted. +* ConnectionManager deletes Requests in that state. +* After this state is set, but before ConnectionManager deletes the Request, +* Request calls user's callback. User can ask Request to change its state +* by calling retry() or pause() methods and Request won't be deleted. +*/ + +enum RequestState { + PROCESSING, + PAUSED, + RETRY, + FINISHED +}; class Request { protected: /** - * Callback, which should be called before Request returns true in handle(). + * Callback, which should be called when Request is finished. * That's the way Requests pass the result to the code which asked to create this request. + * + * @note some Requests use their own callbacks to return something but void *. + * @note callback must be called in finish() or similar method. */ DataCallback _callback; - int32 _id; + /** + * Request state, which is used by ConnectionManager to determine + * whether request might be deleted or it's still working. + * + * State might be changed from outside with finish(), pause() or + * retry() methods. Override these if you want to react to these + * changes correctly. + */ + + RequestState _state; + + /** In RETRY state this indicates whether it's time to call restart(). */ + uint32 _retryInSeconds; public: - Request(DataCallback cb): _callback(cb), _id(-1) {} + Request(DataCallback cb): _callback(cb), _state(PROCESSING), _retryInSeconds(0) {} virtual ~Request() { delete _callback; } - /** - * Method, which does actual work. Depends on what this Request is doing. - */ - + /** Method, which does actual work. Depends on what this Request is doing. */ virtual void handle() = 0; + /** Method, which is called by ConnectionManager when Request's state is RETRY. */ + virtual void handleRetry() { + if (_retryInSeconds > 0) --_retryInSeconds; + else { + _state = PROCESSING; + restart(); + } + } + + /** Method, which is used to restart the Request. */ virtual void restart() = 0; - void setId(int32 id) { _id = id; } + /** Method, which is called to pause the Request. */ + virtual void pause() { _state = PAUSED; } + + /** + * Method, which is called to *interrupt* the Request. + * When it's called, Request must stop its work and + * call the callback to notify user of failure. + */ + virtual void finish() { _state = FINISHED; } + + /** Method, which is called to retry the Request. */ + virtual void retry(uint32 seconds) { + _state = RETRY; + _retryInSeconds = seconds; + } + + /** Returns Request's current state. */ + RequestState state() const { return _state; } }; } //end of namespace Cloud |