mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-13 07:14:59 +00:00
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.
This commit is contained in:
parent
83b349a033
commit
98150beb38
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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]);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -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. */
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -83,6 +83,6 @@ public:
|
||||
long httpResponseCode();
|
||||
};
|
||||
|
||||
} //end of namespace Cloud
|
||||
} //end of namespace Networking
|
||||
|
||||
#endif
|
||||
|
@ -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;
|
||||
|
||||
public:
|
||||
Request(DataCallback cb): _callback(cb), _id(-1) {}
|
||||
virtual ~Request() { delete _callback; }
|
||||
|
||||
/**
|
||||
* Method, which does actual work. Depends on what this Request is doing.
|
||||
* 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), _state(PROCESSING), _retryInSeconds(0) {}
|
||||
virtual ~Request() { delete _callback; }
|
||||
|
||||
/** 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
|
||||
|
BIN
local/onedrive/2/doom.jpg
Normal file
BIN
local/onedrive/2/doom.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 704 KiB |
Loading…
x
Reference in New Issue
Block a user