HaikuDepot : 'Old Client' Handling

The server side has the ability to reject client
requests where the client is no longer supported
because it is presumably too old.  This change
will inform the user when this happens and will
prevent the client from attempting further server
communications within this execution of the
application.
This commit is contained in:
Andrew Lindesay 2018-02-15 22:19:21 +01:00 committed by waddlesplash
parent 3a747315b2
commit 54312619ab
15 changed files with 237 additions and 72 deletions

View File

@ -0,0 +1,22 @@
/*
* Copyright 2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
enum {
MSG_MAIN_WINDOW_CLOSED = 'mwcl',
MSG_PACKAGE_SELECTED = 'pkgs',
MSG_PACKAGE_WORKER_BUSY = 'pkwb',
MSG_PACKAGE_WORKER_IDLE = 'pkwi',
MSG_ADD_VISIBLE_PACKAGES = 'avpk',
MSG_UPDATE_SELECTED_PACKAGE = 'uspk',
MSG_CLIENT_TOO_OLD = 'oldc',
};
#define HD_ERROR_BASE (B_ERRORS_END + 1)
#define HD_NETWORK_INACCESSIBLE (HD_ERROR_BASE + 1)
#define HD_CLIENT_TOO_OLD (HD_ERROR_BASE + 2)
#define HD_ERR_NOT_MODIFIED (HD_ERROR_BASE + 3)
#define HD_ERR_NO_DATA (HD_ERROR_BASE + 4)

View File

@ -101,6 +101,7 @@ Application HaikuDepot :
# network + server
AbstractServerProcess.cpp
AbstractSingleFileServerProcess.cpp
ServerHelper.cpp
ServerSettings.cpp
WebAppInterface.cpp
PkgDataUpdateProcess.cpp

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "AbstractServerProcess.h"
@ -17,7 +17,9 @@
#include <support/ZlibCompressionAlgorithm.h>
#include "HaikuDepotConstants.h"
#include "Logger.h"
#include "ServerHelper.h"
#include "ServerSettings.h"
#include "StandardMetaDataJsonEventListener.h"
#include "StorageUtils.h"
@ -27,8 +29,6 @@
#define MAX_REDIRECTS 3
#define MAX_FAILURES 2
#define HTTP_STATUS_FOUND 302
#define HTTP_STATUS_NOT_MODIFIED 304
// 30 seconds
#define TIMEOUT_MICROSECONDS 3e+7
@ -405,10 +405,13 @@ AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath,
fprintf(stdout, "[%s] did complete streaming data [%"
B_PRIdSSIZE " bytes]\n", Name(), listener.ContentLength());
return B_OK;
} else if (statusCode == HTTP_STATUS_NOT_MODIFIED) {
} else if (statusCode == B_HTTP_STATUS_NOT_MODIFIED) {
fprintf(stdout, "[%s] remote data has not changed since [%s]\n",
Name(), ifModifiedSinceHeader.String());
return APP_ERR_NOT_MODIFIED;
return HD_ERR_NOT_MODIFIED;
} else if (statusCode == B_HTTP_STATUS_PRECONDITION_FAILED) {
ServerHelper::NotifyClientTooOld(responseHeaders);
return HD_CLIENT_TOO_OLD;
} else if (BHttpRequest::IsRedirectionStatusCode(statusCode)) {
if (location.Length() != 0) {
BUrl redirectUrl(result.Url(), location);
@ -477,5 +480,5 @@ AbstractServerProcess::MoveDamagedFileAside(const BPath& currentFilePath)
bool
AbstractServerProcess::IsSuccess(status_t e) {
return e == B_OK || e == APP_ERR_NOT_MODIFIED;
return e == B_OK || e == HD_ERR_NOT_MODIFIED;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@ -15,10 +15,6 @@
#include "Stoppable.h"
#define APP_ERR_NOT_MODIFIED (B_APP_ERROR_BASE + 452)
#define APP_ERR_NO_DATA (B_APP_ERROR_BASE + 453)
typedef enum process_options {
SERVER_PROCESS_NO_NETWORKING = 1 << 0,
SERVER_PROCESS_PREFER_CACHE = 1 << 1,
@ -87,10 +83,8 @@ protected:
const BPath& targetFilePath,
const BUrl& url);
status_t DeleteLocalFile(const BPath& currentFilePath);
status_t MoveDamagedFileAside(
const BPath& currentFilePath);
status_t DeleteLocalFile(const BPath& filePath);
status_t MoveDamagedFileAside(const BPath& filePath);
bool HasOption(uint32 flag);
bool ShouldAttemptNetworkDownload(

View File

@ -1,9 +1,10 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "AbstractSingleFileServerProcess.h"
#include "HaikuDepotConstants.h"
#include "Logger.h"
#include "ServerSettings.h"
#include "StorageUtils.h"
@ -49,18 +50,17 @@ AbstractSingleFileServerProcess::RunInternal()
ServerSettings::CreateFullUrl(urlPathComponent));
}
if (IsSuccess(result) || result == APP_ERR_NOT_MODIFIED) {
if (IsSuccess(result)) {
status_t hasDataResult = StorageUtils::ExistsObject(
localPath, &hasData, NULL, &size);
hasData = hasData && size > 0;
if (hasDataResult == B_OK && !hasData)
result = APP_ERR_NO_DATA;
result = HD_ERR_NO_DATA;
}
if (IsSuccess(result) || result == APP_ERR_NOT_MODIFIED) {
if (IsSuccess(result)) {
if (Logger::IsInfoEnabled())
printf("[%s] did fetch data\n", Name());

View File

@ -1,18 +1,17 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "BulkLoadStateMachine.h"
#include <Autolock.h>
#include <NetworkInterface.h>
#include <NetworkRoster.h>
#include "Logger.h"
#include "PkgDataUpdateProcess.h"
#include "RepositoryDataUpdateProcess.h"
#include "ServerIconExportUpdateProcess.h"
#include "ServerSettings.h"
#include "ServerHelper.h"
BulkLoadStateMachine::BulkLoadStateMachine(Model* model)
@ -186,9 +185,17 @@ BulkLoadStateMachine::Start()
if (!IsRunning()) {
fBulkLoadContext = new BulkLoadContext();
if (ServerSettings::ForceNoNetwork() || !HasNetwork())
if (ServerSettings::IsClientTooOld()) {
printf("bulk load proceeding without network communications "
"because the client is too old\n");
fBulkLoadContext->AddProcessOption(
SERVER_PROCESS_NO_NETWORKING);
}
if (!ServerHelper::IsNetworkAvailable()) {
fBulkLoadContext->AddProcessOption(
SERVER_PROCESS_NO_NETWORKING);
}
if (ServerSettings::PreferCache())
fBulkLoadContext->AddProcessOption(SERVER_PROCESS_PREFER_CACHE);
@ -349,21 +356,3 @@ BulkLoadStateMachine::InitiateBulkPopulatePackagesForAllDepots()
if (0 == fBulkLoadContext->CountPkgProcesses())
ContextPoll();
}
bool
BulkLoadStateMachine::HasNetwork()
{
BNetworkRoster& roster = BNetworkRoster::Default();
BNetworkInterface interface;
uint32 cookie = 0;
while (roster.GetNextInterface(&cookie, interface) == B_OK) {
uint32 flags = interface.Flags();
if ((flags & IFF_LOOPBACK) == 0
&& (flags & (IFF_UP | IFF_LINK)) == (IFF_UP | IFF_LINK)) {
return true;
}
}
return false;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef BULK_LOAD_STATE_MACHINE_H
@ -31,7 +31,6 @@ private:
void ContextPoll();
void SetContextState(bulk_load_state state);
void StopAllProcesses();
bool HasNetwork();
bool CanTransitionTo(
bulk_load_state targetState);

View File

@ -0,0 +1,92 @@
/*
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ServerHelper.h"
#include <stdio.h>
#include <stdlib.h>
#include <Alert.h>
#include <Application.h>
#include <Catalog.h>
#include <NetworkInterface.h>
#include <NetworkRoster.h>
#include "HaikuDepotConstants.h"
#include "ServerSettings.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ServerHelper"
#define KEY_MSG_MINIMUM_VERSION "minimumVersion"
#define KEY_HEADER_MINIMUM_VERSION "X-Desktop-Application-Minimum-Version"
void
ServerHelper::NotifyClientTooOld(const BHttpHeaders& responseHeaders)
{
if (!ServerSettings::IsClientTooOld()) {
ServerSettings::SetClientTooOld();
const char* minimumVersionC = responseHeaders[KEY_HEADER_MINIMUM_VERSION];
BMessage message(MSG_CLIENT_TOO_OLD);
if (minimumVersionC != NULL && strlen(minimumVersionC) != 0) {
message.AddString(KEY_MSG_MINIMUM_VERSION, minimumVersionC);
}
be_app->PostMessage(&message);
}
}
void
ServerHelper::AlertClientTooOld(BMessage* message)
{
BString minimumVersion;
BString alertText;
if (message->FindString(KEY_MSG_MINIMUM_VERSION, &minimumVersion) != B_OK)
minimumVersion = "???";
alertText.SetToFormat(
B_TRANSLATE("This application is too old to communicate with the "
" HaikuDepot server system. Obtain a newer version of HaikuDepot "
" by updating your Haiku system. The minimum version of "
" HaikuDepot required is \"%s\"."), minimumVersion.String());
BAlert* alert = new BAlert(
B_TRANSLATE("client_version_too_old"),
alertText,
B_TRANSLATE("OK"));
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
}
bool
ServerHelper::IsNetworkAvailable()
{
return !ServerSettings::ForceNoNetwork() && IsPlatformNetworkAvailable();
}
bool
ServerHelper::IsPlatformNetworkAvailable()
{
BNetworkRoster& roster = BNetworkRoster::Default();
BNetworkInterface interface;
uint32 cookie = 0;
while (roster.GetNextInterface(&cookie, interface) == B_OK) {
uint32 flags = interface.Flags();
if ((flags & IFF_LOOPBACK) == 0
&& (flags & (IFF_UP | IFF_LINK)) == (IFF_UP | IFF_LINK)) {
return true;
}
}
return false;
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef SERVER_HELPER_H
#define SERVER_HELPER_H
#include <HttpHeaders.h>
#include <Message.h>
class ServerHelper {
public:
static bool IsNetworkAvailable();
static bool IsPlatformNetworkAvailable();
static void NotifyClientTooOld(
const BHttpHeaders& responseHeaders
);
static void AlertClientTooOld(BMessage* message);
};
#endif // SERVER_HELPER_H

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@ -14,6 +14,7 @@
#include <support/StopWatch.h>
#include <support/ZlibCompressionAlgorithm.h>
#include "HaikuDepotConstants.h"
#include "Logger.h"
#include "ServerSettings.h"
#include "StorageUtils.h"
@ -71,7 +72,7 @@ ServerIconExportUpdateProcess::RunInternal()
status_t hasDataResult = HasLocalData(&hasData);
if (IsSuccess(hasDataResult) && !hasData)
result = APP_ERR_NO_DATA;
result = HD_ERR_NO_DATA;
}
}
@ -150,10 +151,7 @@ ServerIconExportUpdateProcess::DownloadAndUnpack()
result = Download(tarGzFilePath);
if (result != APP_ERR_NOT_MODIFIED) {
if (result != B_OK)
return result;
if (result == B_OK) {
printf("delete any existing stored data\n");
StorageUtils::RemoveDirectoryContents(fLocalStorageDirectoryPath);
@ -185,9 +183,9 @@ ServerIconExportUpdateProcess::DownloadAndUnpack()
}
delete tarGzFile;
}
printf("did complete fetching icons\n");
printf("did complete fetching icons\n");
}
return result;
}
@ -227,3 +225,6 @@ ServerIconExportUpdateProcess::Download(BPath& tarGzFilePath)
return DownloadToLocalFileAtomically(tarGzFilePath,
ServerSettings::CreateFullUrl("/__pkgicon/all.tar.gz"));
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@ -10,6 +10,9 @@
#include <AppFileInfo.h>
#include <Application.h>
#include <Autolock.h>
#include <NetworkInterface.h>
#include <NetworkRoster.h>
#include <Roster.h>
#include <Url.h>
@ -24,6 +27,8 @@ pthread_once_t ServerSettings::sUserAgentInitOnce = PTHREAD_ONCE_INIT;
bool ServerSettings::sPreferCache = false;
bool ServerSettings::sDropCache = false;
bool ServerSettings::sForceNoNetwork = false;
bool ServerSettings::sClientTooOld = false;
BLocker ServerSettings::sLock;
status_t
@ -153,3 +158,19 @@ ServerSettings::SetForceNoNetwork(bool value)
{
sForceNoNetwork = value;
}
bool
ServerSettings::IsClientTooOld()
{
BAutolock locker(&sLock);
return sClientTooOld;
}
void
ServerSettings::SetClientTooOld()
{
BAutolock locker(&sLock);
sClientTooOld = true;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@ -8,6 +8,7 @@
#include <File.h>
#include <HttpHeaders.h>
#include <Locker.h>
#include <String.h>
#include <Url.h>
@ -26,17 +27,21 @@ public:
static void SetDropCache(bool value);
static bool ForceNoNetwork();
static void SetForceNoNetwork(bool value);
static bool IsClientTooOld();
static void SetClientTooOld();
private:
static void _InitUserAgent();
static const BString _GetUserAgentVersionString();
static BLocker sLock;
static BUrl sBaseUrl;
static BString sUserAgent;
static pthread_once_t sUserAgentInitOnce;
static bool sPreferCache;
static bool sDropCache;
static bool sForceNoNetwork;
static bool sClientTooOld;
};
#endif // SERVER_SETTINGS_H

View File

@ -23,10 +23,12 @@
#include <UrlProtocolRoster.h>
#include "AutoLocker.h"
#include "HaikuDepotConstants.h"
#include "List.h"
#include "Logger.h"
#include "PackageInfo.h"
#include "ServerSettings.h"
#include "ServerHelper.h"
#define BASEURL_DEFAULT "https://depot.haiku-os.org"
@ -560,6 +562,12 @@ status_t
WebAppInterface::_SendJsonRequest(const char* domain, BString jsonString,
uint32 flags, BMessage& reply) const
{
if (!ServerHelper::IsNetworkAvailable())
return HD_NETWORK_INACCESSIBLE;
if (!ServerSettings::IsClientTooOld())
return HD_CLIENT_TOO_OLD;
if (Logger::IsTraceEnabled())
printf("_SendJsonRequest(%s)\n", jsonString.String());
@ -602,9 +610,19 @@ WebAppInterface::_SendJsonRequest(const char* domain, BString jsonString,
request.Result());
int32 statusCode = result.StatusCode();
if (statusCode != 200) {
printf("Response code: %" B_PRId32 "\n", statusCode);
return B_ERROR;
switch (statusCode) {
case B_HTTP_STATUS_OK:
break;
case B_HTTP_STATUS_PRECONDITION_FAILED:
ServerHelper::NotifyClientTooOld(result.Headers());
return HD_CLIENT_TOO_OLD;
default:
printf("json-rpc request to endpoint [.../%s] failed with http "
"status [%" B_PRId32 "]\n", domain, statusCode);
return B_ERROR;
}
jsonString.SetTo(static_cast<const char*>(replyData.Buffer()),

View File

@ -1,6 +1,6 @@
/*
* Copyright 2013, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@ -24,6 +24,7 @@
#include "FeaturedPackagesView.h"
#include "Logger.h"
#include "MainWindow.h"
#include "ServerHelper.h"
#include "ServerSettings.h"
#include "ScreenshotWindow.h"
@ -103,6 +104,10 @@ App::MessageReceived(BMessage* message)
break;
}
case MSG_CLIENT_TOO_OLD:
ServerHelper::AlertClientTooOld(message);
break;
default:
BApplication::MessageReceived(message);
break;
@ -149,7 +154,7 @@ app_print_help()
fprintf(stdout, "'-v' : allows for the verbosity level to be set.\n");
fprintf(stdout, "'-u' : allows for the haiku depot server url to be\n");
fprintf(stdout, " configured.\n");
fprintf(stdout, "'--nonetworking' : prevents network access.**\n");
fprintf(stdout, "'--nonetworking' : prevents network access.\n");
fprintf(stdout, "'--prefercache' : prefer to get data from cache rather\n");
fprintf(stdout, " then obtain data from the network.**\n");
fprintf(stdout, "'--dropcache' : drop cached data before performing\n");

View File

@ -2,7 +2,7 @@
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2013, Rene Gollent <rene@gollent.com>.
* Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef MAIN_WINDOW_H
@ -15,6 +15,7 @@
#include "PackageAction.h"
#include "PackageActionHandler.h"
#include "PackageInfoListener.h"
#include "HaikuDepotConstants.h"
class BCardLayout;
@ -30,16 +31,6 @@ class ScreenshotWindow;
class WorkStatusView;
enum {
MSG_MAIN_WINDOW_CLOSED = 'mwcl',
MSG_PACKAGE_SELECTED = 'pkgs',
MSG_PACKAGE_WORKER_BUSY = 'pkwb',
MSG_PACKAGE_WORKER_IDLE = 'pkwi',
MSG_ADD_VISIBLE_PACKAGES = 'avpk',
MSG_UPDATE_SELECTED_PACKAGE = 'uspk',
};
class MainWindow : public BWindow, private PackageInfoListener,
private PackageActionHandler {
public: