diff options
author | Dirk Hohndel <dirk@hohndel.org> | 2021-04-16 12:55:05 -0700 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2021-04-19 12:51:01 -0700 |
commit | 766f297bc4c01b81ee11ceb724460240403068a1 (patch) | |
tree | b4cac2a274ccb80f2f5da6d80a5792965f88eeae | |
parent | fe074ccad153791b32c2f62af530091cb025cdee (diff) | |
download | subsurface-766f297bc4c01b81ee11ceb724460240403068a1.tar.gz |
cloudstorage: try alternative server if first connection fails
If we can't reach our preferred server, try using a different one.
The diff makes more sense when ignoring white space.
With this we check the connection to the cloud server much earlier and
in case of failure to connect try a different cloud_base_url.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
-rw-r--r-- | core/checkcloudconnection.cpp | 107 | ||||
-rw-r--r-- | core/checkcloudconnection.h | 1 | ||||
-rw-r--r-- | core/git-access.c | 11 | ||||
-rw-r--r-- | core/git-access.h | 1 |
4 files changed, 84 insertions, 36 deletions
diff --git a/core/checkcloudconnection.cpp b/core/checkcloudconnection.cpp index 714b62836..05e529b90 100644 --- a/core/checkcloudconnection.cpp +++ b/core/checkcloudconnection.cpp @@ -35,48 +35,51 @@ bool CheckCloudConnection::checkServer() if (verbose) fprintf(stderr, "Checking cloud connection...\n"); - QTimer timer; - timer.setSingleShot(true); QEventLoop loop; - QNetworkRequest request; - request.setRawHeader("Accept", "text/plain"); - request.setRawHeader("User-Agent", getUserAgent().toUtf8()); - request.setRawHeader("Client-Id", getUUID().toUtf8()); - request.setUrl(QString(prefs.cloud_base_url) + TEAPOT); QNetworkAccessManager *mgr = new QNetworkAccessManager(); - reply = mgr->get(request); - connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); - connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); - connect(reply, &QNetworkReply::sslErrors, this, &CheckCloudConnection::sslErrors); - for (int seconds = 1; seconds <= prefs.cloud_timeout; seconds++) { - timer.start(1000); // wait the given number of seconds (default 5) - loop.exec(); - if (timer.isActive()) { - // didn't time out, did we get the right response? - timer.stop(); - if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == HTTP_I_AM_A_TEAPOT && - reply->readAll() == QByteArray(MILK)) { - reply->deleteLater(); - mgr->deleteLater(); - if (verbose > 1) - qWarning() << "Cloud storage: successfully checked connection to cloud server"; - return true; + do { + QNetworkRequest request; + request.setRawHeader("Accept", "text/plain"); + request.setRawHeader("User-Agent", getUserAgent().toUtf8()); + request.setRawHeader("Client-Id", getUUID().toUtf8()); + request.setUrl(QString(prefs.cloud_base_url) + TEAPOT); + reply = mgr->get(request); + QTimer timer; + timer.setSingleShot(true); + connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + connect(reply, &QNetworkReply::sslErrors, this, &CheckCloudConnection::sslErrors); + for (int seconds = 1; seconds <= prefs.cloud_timeout; seconds++) { + timer.start(1000); // wait the given number of seconds (default 5) + loop.exec(); + if (timer.isActive()) { + // didn't time out, did we get the right response? + timer.stop(); + if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == HTTP_I_AM_A_TEAPOT && + reply->readAll() == QByteArray(MILK)) { + reply->deleteLater(); + mgr->deleteLater(); + if (verbose) + qWarning() << "Cloud storage: successfully checked connection to cloud server"; + return true; + } + } else if (seconds < prefs.cloud_timeout) { + QString text = tr("Waiting for cloud connection (%n second(s) passed)", "", seconds); + git_storage_update_progress(qPrintable(text)); + } else { + disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit())); + reply->abort(); } - } else if (seconds < prefs.cloud_timeout) { - QString text = tr("Waiting for cloud connection (%n second(s) passed)", "", seconds); - git_storage_update_progress(qPrintable(text)); - } else { - disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit())); - reply->abort(); } - } + if (verbose) + qDebug() << "connection test to cloud server" << prefs.cloud_base_url << "failed" << + reply->error() << reply->errorString() << + reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() << + reply->readAll(); + } while (nextServer()); + // if none of the servers was reachable, update the user and switch to git_local_only git_storage_update_progress(qPrintable(tr("Cloud connection failed"))); git_local_only = true; - if (verbose) - qDebug() << "connection test to cloud server failed" << - reply->error() << reply->errorString() << - reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() << - reply->readAll(); reply->deleteLater(); mgr->deleteLater(); if (verbose) @@ -91,6 +94,38 @@ void CheckCloudConnection::sslErrors(const QList<QSslError> &errorList) qDebug() << err.errorString(); } +bool CheckCloudConnection::nextServer() +{ + struct serverTried { + const char *server; + bool tried; + }; + static struct serverTried cloudServers[] = { + { CLOUD_HOST_EU, false }, + { CLOUD_HOST_US, false } + }; + const char *server = nullptr; + for (int i = 0; i < CLOUD_NUM_HOSTS; i++) { + if (strstr(prefs.cloud_base_url, cloudServers[i].server)) + cloudServers[i].tried = true; + else if (cloudServers[i].tried == false) + server = cloudServers[i].server; + } + if (server) { + int s = strlen(server); + char *baseurl = (char *)malloc(10 + s); + strcpy(baseurl, "https://"); + strncat(baseurl, server, s); + strcat(baseurl, "/"); + qDebug() << "failed to connect to" << prefs.cloud_base_url << "next server to try: " << baseurl; + prefs.cloud_base_url = baseurl; + git_storage_update_progress(qPrintable(tr("Trying different cloud server..."))); + return true; + } + qDebug() << "failed to connect to any of the Subsurface cloud servers, giving up"; + return false; +} + void CheckCloudConnection::pickServer() { QNetworkRequest request(QString(GET_EXTERNAL_IP_API)); diff --git a/core/checkcloudconnection.h b/core/checkcloudconnection.h index 414ddc434..92e1b1a35 100644 --- a/core/checkcloudconnection.h +++ b/core/checkcloudconnection.h @@ -14,6 +14,7 @@ public: void pickServer(); private: QNetworkReply *reply; + bool nextServer(); private slots: void sslErrors(const QList<QSslError> &errorList); diff --git a/core/git-access.c b/core/git-access.c index 31ab9f1d8..c66a24a5d 100644 --- a/core/git-access.c +++ b/core/git-access.c @@ -716,6 +716,8 @@ int sync_with_remote(git_repository *repo, const char *remote, const char *branc return 0; } + // we know that we already checked for the cloud server, but to give a decent warning message + // here in case none of them are reachable, let's check one more time if (is_subsurface_cloud && !canReachCloudServer()) { // this is not an error, just a warning message, so return 0 SSRF_INFO("git storage: cannot connect to remote server"); @@ -723,6 +725,7 @@ int sync_with_remote(git_repository *repo, const char *remote, const char *branc git_storage_update_progress(translate("gettextFromC", "Can't reach cloud server, working with local data")); return 0; } + if (verbose) SSRF_INFO("git storage: fetch remote %s\n", git_remote_url(origin)); git_fetch_options opts = GIT_FETCH_OPTIONS_INIT; @@ -1042,6 +1045,14 @@ static struct git_repository *is_remote_git_repository(char *remote, const char * this is used to create more user friendly error message and warnings */ is_subsurface_cloud = strstr(remote, prefs.cloud_base_url) != NULL; + /* if we are planning to access the server, make sure it's available and try to + * pick one of the alternative servers if necessary */ + if (is_subsurface_cloud && !git_local_only) { + // since we know that this is Subsurface cloud storage, we don't have to + // worry about the local directory name changing if we end up with a different + // cloud_base_url... the algorithm normalizes those URLs + (void)canReachCloudServer(); + } return get_remote_repo(localdir, remote, branch); } diff --git a/core/git-access.h b/core/git-access.h index 210ebb2fd..0ac5294c6 100644 --- a/core/git-access.h +++ b/core/git-access.h @@ -15,6 +15,7 @@ extern "C" { #include <stdbool.h> #endif +#define CLOUD_NUM_HOSTS 2 #define CLOUD_HOST_US "ssrf-cloud-us.subsurface-divelog.org" #define CLOUD_HOST_EU "ssrf-cloud-eu.subsurface-divelog.org" #define CLOUD_HOST_PATTERN "ssrf-cloud-..\\.subsurface-divelog\\.org" |