Skip to content

Commit 56a4b4b

Browse files
committed
dbhub: Make network code less centralised
This adds the possibility to handle network replies individually instead of routing all of them through a central handler routine. The centralised approach made the code more complicated than necessary and would inevitably lead to confusion when sending similar requests simultaneously.
1 parent ddf5117 commit 56a4b4b

File tree

9 files changed

+145
-269
lines changed

9 files changed

+145
-269
lines changed

src/MainWindow.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,21 @@ void MainWindow::init()
9696

9797
// Automatic update check
9898
#ifdef CHECKNEWVERSION
99-
connect(&RemoteNetwork::get(), &RemoteNetwork::networkReady, []() {
99+
connect(&RemoteNetwork::get(), &RemoteNetwork::networkReady, [this]() {
100100
// Check for a new version if automatic update check aren't disabled in the settings dialog
101101
if(Settings::getValue("checkversion", "enabled").toBool())
102-
RemoteNetwork::get().fetch(QUrl("https://download.sqlitebrowser.org/currentrelease"), RemoteNetwork::RequestTypeNewVersionCheck);
102+
{
103+
RemoteNetwork::get().fetch(QUrl("https://download.sqlitebrowser.org/currentrelease"), RemoteNetwork::RequestTypeCustom,
104+
QString(), QVariant(), [this](const QByteArray& reply) {
105+
QList<QByteArray> info = reply.split('\n');
106+
if(info.size() >= 2)
107+
{
108+
QString version = info.at(0).trimmed();
109+
QString url = info.at(1).trimmed();
110+
checkNewVersion(version, url);
111+
}
112+
});
113+
}
103114
});
104115
#endif
105116

@@ -386,7 +397,6 @@ void MainWindow::init()
386397
connect(ui->dbTreeWidget->selectionModel(), &QItemSelectionModel::currentChanged, this, &MainWindow::changeTreeSelection);
387398
connect(ui->dockEdit, &QDockWidget::visibilityChanged, this, &MainWindow::toggleEditDock);
388399
connect(remoteDock, SIGNAL(openFile(QString)), this, SLOT(fileOpen(QString)));
389-
connect(&RemoteNetwork::get(), &RemoteNetwork::gotCurrentVersion, this, &MainWindow::checkNewVersion);
390400
connect(ui->actionDropQualifiedCheck, &QAction::toggled, dbStructureModel, &DbStructureModel::setDropQualifiedNames);
391401
connect(ui->actionEnquoteNamesCheck, &QAction::toggled, dbStructureModel, &DbStructureModel::setDropEnquotedNames);
392402
connect(&db, &DBBrowserDB::databaseInUseChanged, this, &MainWindow::updateDatabaseBusyStatus);

src/RemoteDock.cpp

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include <QUrl>
77
#include <QUrlQuery>
88

9+
#include <json.hpp>
10+
911
#include "RemoteDock.h"
1012
#include "ui_RemoteDock.h"
1113
#include "Settings.h"
@@ -17,6 +19,8 @@
1719
#include "RemotePushDialog.h"
1820
#include "PreferencesDialog.h"
1921

22+
using json = nlohmann::json;
23+
2024
RemoteDock::RemoteDock(MainWindow* parent)
2125
: QDialog(parent),
2226
ui(new Ui::RemoteDock),
@@ -46,9 +50,6 @@ RemoteDock::RemoteDock(MainWindow* parent)
4650
// Whenever a new directory listing has been parsed, check if it was a new root dir and, if so, open the user's directory
4751
connect(remoteModel, &RemoteModel::directoryListingParsed, this, &RemoteDock::newDirectoryNode);
4852

49-
// Show metadata for a database when we get it
50-
connect(&RemoteNetwork::get(), &RemoteNetwork::gotMetadata, this, &RemoteDock::showMetadata);
51-
5253
// When the Preferences link is clicked in the no-certificates-label, open the preferences dialog. For other links than the ones we know,
5354
// just open them in a web browser
5455
connect(ui->labelNoCert, &QLabel::linkActivated, [this](const QString& link) {
@@ -448,30 +449,33 @@ void RemoteDock::fileOpened(const QString& filename)
448449

449450
void RemoteDock::refreshMetadata(const QString& username, const QString& dbname)
450451
{
452+
// Make request for meta data
451453
QUrl url(RemoteNetwork::get().getInfoFromClientCert(remoteModel->currentClientCertificate(), RemoteNetwork::CertInfoServer) + "/metadata/get");
452454
QUrlQuery query;
453455
query.addQueryItem("username", username);
454456
query.addQueryItem("folder", "/");
455457
query.addQueryItem("dbname", dbname);
456458
url.setQuery(query);
457-
RemoteNetwork::get().fetch(url.toString(), RemoteNetwork::RequestTypeMetadata, remoteModel->currentClientCertificate());
458-
}
459+
RemoteNetwork::get().fetch(url.toString(), RemoteNetwork::RequestTypeCustom, remoteModel->currentClientCertificate(), [this](const QByteArray& reply) {
460+
// Read and check results
461+
json obj = json::parse(reply, nullptr, false);
462+
if(obj.is_discarded() || !obj.is_object())
463+
return;
459464

460-
void RemoteDock::showMetadata(const std::vector<RemoteMetadataBranchInfo>& branches, const std::string& commits,
461-
const std::vector<RemoteMetadataReleaseInfo>& /*releases*/, const std::vector<RemoteMetadataReleaseInfo>& /*tags*/,
462-
const std::string& /*default_branch*/, const std::string& web_page)
463-
{
464-
// Store all the commit information as-is
465-
current_commit_json = commits;
465+
// Store all the commit information as-is
466+
json obj_commits = obj["commits"];
467+
current_commit_json = obj_commits.dump();
466468

467-
// Store the link to the web page in the action for opening that link in a browser
468-
ui->actionDatabaseOpenBrowser->setData(QString::fromStdString(web_page));
469+
// Store the link to the web page in the action for opening that link in a browser
470+
ui->actionDatabaseOpenBrowser->setData(QString::fromStdString(obj["web_page"]));
469471

470-
// Fill branches combo box
471-
ui->comboDatabaseBranch->clear();
472-
for(const auto& branch : branches)
473-
ui->comboDatabaseBranch->addItem(QString::fromStdString(branch.name), QString::fromStdString(branch.commit_id));
474-
ui->comboDatabaseBranch->setCurrentIndex(ui->comboDatabaseBranch->findText(ui->editDatabaseBranch->text()));
472+
// Fill branches combo box
473+
json obj_branches = obj["branches"];
474+
ui->comboDatabaseBranch->clear();
475+
for(auto it=obj_branches.cbegin();it!=obj_branches.cend();++it)
476+
ui->comboDatabaseBranch->addItem(QString::fromStdString(it.key()), QString::fromStdString(it.value()["commit"]));
477+
ui->comboDatabaseBranch->setCurrentIndex(ui->comboDatabaseBranch->findText(ui->editDatabaseBranch->text()));
478+
});
475479
}
476480

477481
void RemoteDock::deleteLocalDatabase(const QModelIndex& index)

src/RemoteDock.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,6 @@ private slots:
4444
void newDirectoryNode(const QModelIndex& parent);
4545
void switchToMainView();
4646
void openLocalFile(const QModelIndex& idx);
47-
void showMetadata(const std::vector<RemoteMetadataBranchInfo>& branches, const std::string& commits,
48-
const std::vector<RemoteMetadataReleaseInfo>& releases, const std::vector<RemoteMetadataReleaseInfo>& tags,
49-
const std::string& default_branch, const std::string& web_page);
5047
void deleteLocalDatabase(const QModelIndex& index);
5148
void openCurrentDatabaseInBrowser() const;
5249
void refresh();

src/RemoteModel.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,6 @@ RemoteModel::RemoteModel(QObject* parent) :
103103
headerList({tr("Name"), tr("Last modified"), tr("Size"), tr("Commit")}),
104104
rootItem(new RemoteModelItem())
105105
{
106-
// Set up signals
107-
connect(&RemoteNetwork::get(), &RemoteNetwork::gotDirList, this, &RemoteModel::parseDirectoryListing);
108106
}
109107

110108
RemoteModel::~RemoteModel()
@@ -128,18 +126,19 @@ void RemoteModel::setNewRootDir(const QString& url, const QString& cert)
128126
void RemoteModel::refresh()
129127
{
130128
// Fetch root directory and put the reply data under the root item
131-
RemoteNetwork::get().fetch(currentRootDirectory, RemoteNetwork::RequestTypeDirectory, currentClientCert, QModelIndex());
129+
RemoteNetwork::get().fetch(currentRootDirectory, RemoteNetwork::RequestTypeCustom, currentClientCert, [this](const QByteArray& reply) {
130+
parseDirectoryListing(reply, QModelIndex());
131+
});
132132
}
133133

134-
void RemoteModel::parseDirectoryListing(const QString& text, const QVariant& userdata)
134+
void RemoteModel::parseDirectoryListing(const QString& text, QModelIndex parent)
135135
{
136136
// Load new JSON root document assuming it's an array
137137
json array = json::parse(text.toStdString(), nullptr, false);
138138
if(array.is_discarded() || !array.is_array())
139139
return;
140140

141141
// Get model index to store the new data under
142-
QModelIndex parent = userdata.toModelIndex();
143142
RemoteModelItem* parentItem = const_cast<RemoteModelItem*>(modelIndexToItem(parent));
144143

145144
// An invalid model index indicates that this is a new root item. This means the old one needs to be entirely deleted first.
@@ -306,7 +305,9 @@ void RemoteModel::fetchMore(const QModelIndex& parent)
306305

307306
// Fetch item URL
308307
item->setFetchedDirectoryList(true);
309-
RemoteNetwork::get().fetch(item->value(RemoteModelColumnUrl).toUrl(), RemoteNetwork::RequestTypeDirectory, currentClientCert, parent);
308+
RemoteNetwork::get().fetch(item->value(RemoteModelColumnUrl).toUrl(), RemoteNetwork::RequestTypeCustom, currentClientCert, [this, parent](const QByteArray& reply) {
309+
parseDirectoryListing(reply, parent);
310+
});
310311
}
311312

312313
const QString& RemoteModel::currentClientCertificate() const

src/RemoteModel.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,8 @@ class RemoteModel : public QAbstractItemModel
9292
void directoryListingParsed(QModelIndex parent);
9393

9494
private slots:
95-
// This is called whenever a network reply containing a directory listing arrives. json contains the reply data, userdata
96-
// contains some custom data passed to the request. In this case we expect this to be the model index of the parent tree item.
97-
void parseDirectoryListing(const QString& text, const QVariant& userdata);
95+
// This is called whenever a network reply containing a directory listing arrives
96+
void parseDirectoryListing(const QString& text, QModelIndex parent);
9897

9998
private:
10099
// The header list is a list of column titles

src/RemoteNetwork.cpp

Lines changed: 38 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ RemoteNetwork::RemoteNetwork() :
5353
reloadSettings();
5454

5555
// Set up signals
56-
connect(m_manager, &QNetworkAccessManager::finished, this, &RemoteNetwork::gotReply);
5756
connect(m_manager, &QNetworkAccessManager::encrypted, this, &RemoteNetwork::gotEncrypted);
5857
connect(m_manager, &QNetworkAccessManager::sslErrors, this, &RemoteNetwork::gotError);
5958
}
@@ -161,35 +160,6 @@ void RemoteNetwork::gotEncrypted(QNetworkReply* reply)
161160

162161
void RemoteNetwork::gotReply(QNetworkReply* reply)
163162
{
164-
// Check if request was successful
165-
if(reply->error() != QNetworkReply::NoError)
166-
{
167-
// Do not show error message when operation was cancelled on purpose
168-
if(reply->error() != QNetworkReply::OperationCanceledError)
169-
{
170-
QMessageBox::warning(nullptr, qApp->applicationName(),
171-
reply->errorString() + "\n" + reply->readAll());
172-
}
173-
174-
reply->deleteLater();
175-
return;
176-
}
177-
178-
// Check for redirect
179-
QString redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toString();
180-
if(!redirectUrl.isEmpty())
181-
{
182-
// Avoid redirect loop
183-
if(reply->url() == redirectUrl)
184-
{
185-
reply->deleteLater();
186-
return;
187-
}
188-
fetch(redirectUrl, static_cast<RequestType>(reply->property("type").toInt()), reply->property("certfile").toString(), reply->property("userdata"));
189-
reply->deleteLater();
190-
return;
191-
}
192-
193163
// What type of data is this?
194164
RequestType type = static_cast<RequestType>(reply->property("type").toInt());
195165

@@ -222,56 +192,6 @@ void RemoteNetwork::gotReply(QNetworkReply* reply)
222192
reply);
223193
}
224194
break;
225-
case RequestTypeDirectory:
226-
emit gotDirList(reply->readAll(), reply->property("userdata"));
227-
break;
228-
case RequestTypeNewVersionCheck:
229-
{
230-
QString version = reply->readLine().trimmed();
231-
QString url = reply->readLine().trimmed();
232-
emit gotCurrentVersion(version, url);
233-
break;
234-
}
235-
case RequestTypeLicenceList:
236-
{
237-
// Read and check results
238-
json obj = json::parse(reply->readAll(), nullptr, false);
239-
if(obj.is_discarded() || !obj.is_object())
240-
break;
241-
242-
// Parse data and build ordered licence map: order -> (short name, long name)
243-
std::map<int, std::pair<std::string, std::string>> licences;
244-
for(auto it=obj.cbegin();it!=obj.cend();++it)
245-
licences.insert({it.value()["order"], {it.key(), it.value()["full_name"]}});
246-
247-
// Convert the map into an ordered vector and send it to anyone who's interested
248-
std::vector<std::pair<std::string, std::string>> licence_list;
249-
std::transform(licences.begin(), licences.end(), std::back_inserter(licence_list), [](const std::pair<int, std::pair<std::string, std::string>>& it) {
250-
return it.second;
251-
});
252-
emit gotLicenceList(licence_list);
253-
break;
254-
}
255-
case RequestTypeBranchList:
256-
{
257-
// Read and check results
258-
json obj = json::parse(reply->readAll(), nullptr, false);
259-
if(obj.is_discarded() || !obj.is_object())
260-
break;
261-
json obj_branches = obj["branches"];
262-
263-
// Parse data and assemble branch list
264-
std::vector<std::string> branches;
265-
for(auto it=obj_branches.cbegin();it!=obj_branches.cend();++it)
266-
branches.push_back(it.key());
267-
268-
// Get default branch
269-
std::string default_branch = (obj.contains("default_branch") && !obj["default_branch"].empty()) ? obj["default_branch"] : "master";
270-
271-
// Send branch list to anyone who is interested
272-
emit gotBranchList(branches, default_branch);
273-
break;
274-
}
275195
case RequestTypePush:
276196
{
277197
// Read and check results
@@ -288,41 +208,6 @@ void RemoteNetwork::gotReply(QNetworkReply* reply)
288208
reply->property("source_file").toString());
289209
break;
290210
}
291-
case RequestTypeMetadata:
292-
{
293-
// Read and check results
294-
json obj = json::parse(reply->readAll(), nullptr, false);
295-
if(obj.is_discarded() || !obj.is_object())
296-
break;
297-
298-
// Extract and convert data
299-
json obj_branches = obj["branches"];
300-
json obj_commits = obj["commits"];
301-
json obj_releases = obj["releases"];
302-
json obj_tags = obj["tags"];
303-
std::string default_branch = (obj.contains("default_branch") && !obj["default_branch"].empty()) ? obj["default_branch"] : "master";
304-
std::vector<RemoteMetadataBranchInfo> branches;
305-
for(auto it=obj_branches.cbegin();it!=obj_branches.cend();++it)
306-
branches.emplace_back(it.key(), it.value()["commit"], it.value()["description"], it.value()["commit_count"]);
307-
std::vector<RemoteMetadataReleaseInfo> releases;
308-
for(auto it=obj_releases.cbegin();it!=obj_releases.cend();++it)
309-
{
310-
releases.emplace_back(it.key(), it.value()["commit"], it.value()["date"],
311-
it.value()["description"], it.value()["email"],
312-
it.value()["name"], it.value()["size"]);
313-
}
314-
std::vector<RemoteMetadataReleaseInfo> tags;
315-
for(auto it=obj_tags.cbegin();it!=obj_tags.cend();++it)
316-
{
317-
tags.emplace_back(it.key(), it.value()["commit"], it.value()["date"],
318-
it.value()["description"], it.value()["email"],
319-
it.value()["name"], 0);
320-
}
321-
322-
// Send data list to anyone who is interested
323-
emit gotMetadata(branches, obj_commits.dump(), releases, tags, default_branch, obj["web_page"]);
324-
break;
325-
}
326211
case RequestTypeDownload:
327212
{
328213
// It's a download
@@ -496,7 +381,8 @@ void RemoteNetwork::prepareProgressDialog(QNetworkReply* reply, bool upload, con
496381
connect(reply, &QNetworkReply::downloadProgress, this, &RemoteNetwork::updateProgress);
497382
}
498383

499-
void RemoteNetwork::fetch(const QUrl& url, RequestType type, const QString& clientCert, QVariant userdata)
384+
void RemoteNetwork::fetch(const QUrl& url, RequestType type, const QString& clientCert,
385+
std::function<void(QByteArray)> when_finished)
500386
{
501387
// Check if network is accessible. If not, abort right here
502388
if(m_manager->networkAccessible() == QNetworkAccessManager::NotAccessible)
@@ -509,6 +395,9 @@ void RemoteNetwork::fetch(const QUrl& url, RequestType type, const QString& clie
509395
QNetworkRequest request;
510396
request.setUrl(url);
511397
request.setRawHeader("User-Agent", QString("%1 %2").arg(qApp->organizationName(), APP_VERSION).toUtf8());
398+
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
399+
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
400+
#endif
512401

513402
// Set SSL configuration when trying to access a file via the HTTPS protocol.
514403
// Skip this step when no client certificate was specified. In this case the default HTTPS configuration is used.
@@ -527,7 +416,20 @@ void RemoteNetwork::fetch(const QUrl& url, RequestType type, const QString& clie
527416
QNetworkReply* reply = m_manager->get(request);
528417
reply->setProperty("type", type);
529418
reply->setProperty("certfile", clientCert);
530-
reply->setProperty("userdata", userdata);
419+
420+
// Hook up custom handler when there is one and global handler otherwise
421+
if(when_finished)
422+
{
423+
connect(reply, &QNetworkReply::finished, reply, [this, when_finished, reply]() {
424+
if(handleReply(reply))
425+
when_finished(reply->readAll());
426+
});
427+
} else {
428+
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
429+
if(handleReply(reply))
430+
gotReply(reply);
431+
});
432+
}
531433

532434
// Initialise the progress dialog for this request, but only if this is a database file or a download.
533435
// Directory listing and similar are small enough to be loaded without progress dialog.
@@ -633,3 +535,22 @@ void RemoteNetwork::clearAccessCache(const QString& clientCert)
633535
m_manager->clearAccessCache();
634536
}
635537
}
538+
539+
bool RemoteNetwork::handleReply(QNetworkReply* reply)
540+
{
541+
// Check if request was successful
542+
if(reply->error() != QNetworkReply::NoError)
543+
{
544+
// Do not show error message when operation was cancelled on purpose
545+
if(reply->error() != QNetworkReply::OperationCanceledError)
546+
{
547+
QMessageBox::warning(nullptr, qApp->applicationName(),
548+
reply->errorString() + "\n" + reply->readAll());
549+
}
550+
551+
reply->deleteLater();
552+
return false;
553+
}
554+
555+
return true;
556+
}

0 commit comments

Comments
 (0)