forked from Silverfish/proton-bridge
GODT-1917: gRPC service should use random port.
WIP: bridge-gui wait and parse gRPC service config fie.
This commit is contained in:
@ -107,13 +107,13 @@ add_library(bridgepp
|
||||
bridgepp/Exception/Exception.h bridgepp/Exception/Exception.cpp
|
||||
bridgepp/GRPC/GRPCClient.cpp bridgepp/GRPC/GRPCClient.h
|
||||
bridgepp/GRPC/EventFactory.cpp bridgepp/GRPC/EventFactory.h
|
||||
bridgepp/GRPC/GRPCConfig.cpp bridgepp/GRPC/GRPCConfig.h
|
||||
bridgepp/GRPC/GRPCUtils.cpp bridgepp/GRPC/GRPCUtils.h
|
||||
${PROTO_CPP_FILE} ${PROTO_H_FILE} ${GRPC_CPP_FILE} ${GRPC_H_FILE}
|
||||
bridgepp/Log/Log.h bridgepp/Log/Log.cpp
|
||||
bridgepp/ProcessMonitor.cpp bridgepp/ProcessMonitor.h
|
||||
bridgepp/User/User.cpp bridgepp/User/User.h
|
||||
bridgepp/Worker/Worker.h bridgepp/Worker/Overseer.h bridgepp/Worker/Overseer.cpp
|
||||
)
|
||||
bridgepp/Worker/Worker.h bridgepp/Worker/Overseer.h bridgepp/Worker/Overseer.cpp)
|
||||
|
||||
target_include_directories(bridgepp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
|
||||
@ -43,6 +43,54 @@ int const maxCertificateWaitMsecs = 60 * 1000; ///< Amount of time we wait for h
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
//
|
||||
//****************************************************************************************************************************************************
|
||||
void GRPCClient::removeServiceConfigFile()
|
||||
{
|
||||
QString const path = serviceConfigPath();
|
||||
if (!QFile(path).exists())
|
||||
return;
|
||||
if (!QFile().remove(path))
|
||||
throw Exception("Could not remove gRPC service config file.");
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] timeoutMs The timeout in milliseconds
|
||||
/// \return The service config.
|
||||
//****************************************************************************************************************************************************
|
||||
GRPCConfig GRPCClient::waitAndRetrieveServiceConfig(qint64 timeoutMs)
|
||||
{
|
||||
QString const path = serviceConfigPath();
|
||||
QFile file(path);
|
||||
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
bool found = false;
|
||||
while (true)
|
||||
{
|
||||
if (file.exists())
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (timer.elapsed() > timeoutMs)
|
||||
break;
|
||||
QThread::msleep(100);
|
||||
}
|
||||
|
||||
if (!found)
|
||||
throw Exception("Server did not provide gRPC service configuration in time.");
|
||||
|
||||
GRPCConfig sc;
|
||||
if (!sc.load(path))
|
||||
throw Exception("The gRPC service configuration file is invalid.");
|
||||
|
||||
return sc;
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \brief wait for certificate generation by Bridge
|
||||
/// \return server certificate generated by Bridge
|
||||
@ -110,14 +158,15 @@ void GRPCClient::setLog(Log *log)
|
||||
/// \param[out] outError If the function returns false, this variable contains a description of the error.
|
||||
/// \return true iff the connection was successful.
|
||||
//****************************************************************************************************************************************************
|
||||
bool GRPCClient::connectToServer(QString &outError)
|
||||
bool GRPCClient::connectToServer(GRPCConfig const &config, QString &outError)
|
||||
{
|
||||
try
|
||||
{
|
||||
SslCredentialsOptions opts;
|
||||
opts.pem_root_certs += this->getServerCertificate();
|
||||
|
||||
channel_ = CreateChannel("127.0.0.1:9292", grpc::SslCredentials(opts));
|
||||
QString const address = QString("127.0.0.1:%1").arg(config.port);
|
||||
channel_ = CreateChannel(address.toStdString(), grpc::SslCredentials(opts));
|
||||
if (!channel_)
|
||||
throw Exception("Channel creation failed.");
|
||||
|
||||
@ -130,7 +179,7 @@ bool GRPCClient::connectToServer(QString &outError)
|
||||
while (true)
|
||||
{
|
||||
if (log_)
|
||||
log_->debug(QString("Connection to gRPC server. attempt #%1").arg(++i));
|
||||
log_->debug(QString("Connection to gRPC server at %1. attempt #%2").arg(address).arg(++i));
|
||||
|
||||
if (channel_->WaitForConnected(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_seconds(5, GPR_TIMESPAN))))
|
||||
break; // connection established.
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
|
||||
#include "../User/User.h"
|
||||
#include "../Log/Log.h"
|
||||
#include "GRPCConfig.h"
|
||||
#include "bridge.grpc.pb.h"
|
||||
#include "grpc++/grpc++.h"
|
||||
|
||||
@ -46,6 +47,10 @@ typedef grpc::Status (grpc::Bridge::Stub::*StringParamMethod)(grpc::ClientContex
|
||||
class GRPCClient : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public: // static member functions
|
||||
static void removeServiceConfigFile(); ///< Delete the service config file.
|
||||
static GRPCConfig waitAndRetrieveServiceConfig(qint64 timeoutMs); ///< Wait and retrieve the service configuration.
|
||||
|
||||
public: // member functions.
|
||||
GRPCClient() = default; ///< Default constructor.
|
||||
GRPCClient(GRPCClient const &) = delete; ///< Disabled copy-constructor.
|
||||
@ -54,7 +59,7 @@ public: // member functions.
|
||||
GRPCClient &operator=(GRPCClient const &) = delete; ///< Disabled assignment operator.
|
||||
GRPCClient &operator=(GRPCClient &&) = delete; ///< Disabled move assignment operator.
|
||||
void setLog(Log *log); ///< Set the log for the client.
|
||||
bool connectToServer(QString &outError); ///< Establish connection to the gRPC server.
|
||||
bool connectToServer(GRPCConfig const &config, QString &outError); ///< Establish connection to the gRPC server.
|
||||
|
||||
grpc::Status addLogEntry(Log::Level level, QString const &package, QString const &message); ///< Performs the "AddLogEntry" gRPC call.
|
||||
grpc::Status guiReady(); ///< performs the "GuiReady" gRPC call.
|
||||
@ -218,6 +223,7 @@ private:
|
||||
grpc::Status methodWithStringParam(StringParamMethod method, QString const &str); ///< Perform a gRPC call that takes a string as a parameter and returns an Empty.
|
||||
SPUser parseGRPCUser(grpc::User const &grpcUser); ///< Parse a gRPC user struct and return a User.
|
||||
|
||||
|
||||
std::string getServerCertificate(); ///< Wait until server certificates is generated and retrieve it.
|
||||
void processAppEvent(grpc::AppEvent const &event); ///< Process an 'App' event.
|
||||
void processLoginEvent(grpc::LoginEvent const &event); ///< Process a 'Login' event.
|
||||
|
||||
@ -0,0 +1,133 @@
|
||||
// Copyright (c) 2022 Proton AG
|
||||
//
|
||||
// This file is part of Proton Mail Bridge.
|
||||
//
|
||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#include "GRPCConfig.h"
|
||||
#include "../Exception/Exception.h"
|
||||
|
||||
|
||||
using namespace bridgepp;
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
Exception const invalidFileException("The service configuration file is invalid"); // Exception for invalid config.
|
||||
Exception const couldNotSaveException("The service configuration file could not be saved"); ///< Exception for write errors.
|
||||
QString const keyPort = "port"; ///< The JSON key for the port.
|
||||
QString const keyCert = "cert"; ///< The JSON key for the TLS certificate.
|
||||
QString const keyToken = "token"; ///< The JSON key for the identification token.
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \brief read a string value from a JSON object.
|
||||
///
|
||||
/// This function throws an Exception in case of error
|
||||
///
|
||||
/// \param[in] object The JSON object containing the value.
|
||||
/// \param[in] key The key under which the value is stored.
|
||||
//****************************************************************************************************************************************************
|
||||
QString jsonStringValue(QJsonObject const &object, QString const &key)
|
||||
{
|
||||
QJsonValue const v = object[key];
|
||||
if (!v.isString())
|
||||
throw invalidFileException;
|
||||
return v.toString();
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \brief read a string value from a JSON object.
|
||||
///
|
||||
/// This function throws an Exception in case of error.
|
||||
///
|
||||
/// \param[in] object The JSON object containing the value.
|
||||
/// \param[in] key The key under which the value is stored.
|
||||
//****************************************************************************************************************************************************
|
||||
qint32 jsonIntValue(QJsonObject const &object, QString const &key)
|
||||
{
|
||||
QJsonValue const v = object[key];
|
||||
if (!v.isDouble())
|
||||
throw invalidFileException;
|
||||
return v.toInt();
|
||||
}
|
||||
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] path The path of the file to load from.
|
||||
/// \param[out] outError if not null and an error occurs, this variable contains a description of the error.
|
||||
/// \return true iff the operation was successful.
|
||||
//****************************************************************************************************************************************************
|
||||
bool GRPCConfig::load(QString const &path, QString *outError)
|
||||
{
|
||||
try
|
||||
{
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
throw Exception("Could not open gRPC service config file.");
|
||||
|
||||
QJsonDocument const doc = QJsonDocument::fromJson(file.readAll());
|
||||
QJsonObject const object = doc.object();
|
||||
port = jsonIntValue(object, keyPort);
|
||||
cert = jsonStringValue(object, keyCert);
|
||||
token = jsonStringValue(object, keyToken);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception const &e)
|
||||
{
|
||||
if (outError)
|
||||
*outError = e.qwhat();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] path The path of the file to write to.
|
||||
/// \param[out] outError if not null and an error occurs, this variable contains a description of the error.
|
||||
/// \return true iff the operation was successful.
|
||||
//****************************************************************************************************************************************************
|
||||
bool GRPCConfig::save(QString const &path, QString *outError)
|
||||
{
|
||||
try
|
||||
{
|
||||
QJsonObject const object;
|
||||
object[keyPort] = port;
|
||||
object[keyCert] = cert;
|
||||
object[keyToken] = token;
|
||||
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||
throw couldNotSaveException;
|
||||
|
||||
QByteArray const array = QJsonDocument(object).toJson();
|
||||
if (array.size() != file.write(array))
|
||||
throw couldNotSaveException;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception const &e)
|
||||
{
|
||||
if (outError)
|
||||
*outError = e.qwhat();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2022 Proton AG
|
||||
//
|
||||
// This file is part of Proton Mail Bridge.
|
||||
//
|
||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#ifndef BRIDGE_PP_GRPC_CONFIG_H
|
||||
#define BRIDGE_PP_GRPC_CONFIG_H
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// Service configuration class.
|
||||
//****************************************************************************************************************************************************
|
||||
struct GRPCConfig
|
||||
{
|
||||
public: // data members
|
||||
qint32 port; ///< The port.
|
||||
QString cert; ///< The server TLS certificate.
|
||||
QString token; ///< The identification token.
|
||||
|
||||
bool load(QString const &path, QString *outError = nullptr); ///< Load the service config from file
|
||||
bool save(QString const &path, QString *outError = nullptr); ///< Save the service config to file
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //BRIDGE_PP_GRPC_CONFIG_H
|
||||
@ -38,6 +38,14 @@ QString serverCertificateFilename()
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \return the service config file name
|
||||
//****************************************************************************************************************************************************
|
||||
QString serviceConfigFilename()
|
||||
{
|
||||
return "grpcServiceConfig.json";
|
||||
}
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
//
|
||||
//****************************************************************************************************************************************************
|
||||
@ -50,6 +58,15 @@ QString serverKeyFilename()
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \return The absolute path of the service config path.
|
||||
//****************************************************************************************************************************************************
|
||||
QString serviceConfigPath()
|
||||
{
|
||||
return QDir(userConfigDir()).absoluteFilePath(serviceConfigFilename());
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \return The absolute path of the server certificate.
|
||||
//****************************************************************************************************************************************************
|
||||
|
||||
@ -31,7 +31,7 @@ namespace bridgepp
|
||||
|
||||
typedef std::shared_ptr<grpc::StreamEvent> SPStreamEvent; ///< Type definition for shared pointer to grpc::StreamEvent.
|
||||
|
||||
|
||||
QString serviceConfigPath(); ///< Return the path of the service config file.
|
||||
QString serverCertificatePath(); ///< Return the path of the server certificate.
|
||||
QString serverKeyPath(); ///< Return the path of the server key.
|
||||
grpc::LogLevel logLevelToGRPC(Log::Level level); ///< Convert a Log::Level to gRPC enum value.
|
||||
|
||||
Reference in New Issue
Block a user