2022-10-20 11:49:27 +00:00
|
|
|
// Copyright (C) 2022 The Qt Company Ltd.
|
|
|
|
// Copyright (C) 2019 Alexey Edelev <semlanik@gmail.com>
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
|
|
|
|
2023-02-02 09:03:34 +00:00
|
|
|
#include <QtGrpc/private/qabstractgrpcchannel_p.h>
|
2024-07-03 09:02:17 +00:00
|
|
|
#include <QtGrpc/private/qtgrpcglobal_p.h>
|
|
|
|
#include <QtGrpc/qgrpcclientbase.h>
|
2024-07-30 11:47:44 +00:00
|
|
|
#include <QtGrpc/qgrpcoperation.h>
|
2022-10-20 11:49:27 +00:00
|
|
|
|
2024-07-30 11:47:44 +00:00
|
|
|
#include <QtProtobuf/qprotobufmessage.h>
|
2024-07-03 09:02:17 +00:00
|
|
|
#include <QtProtobuf/qprotobufserializer.h>
|
2023-01-02 15:06:11 +00:00
|
|
|
|
2024-05-29 14:31:05 +00:00
|
|
|
#include <QtCore/private/qminimalflatset_p.h>
|
2024-07-03 09:02:17 +00:00
|
|
|
#include <QtCore/private/qobject_p.h>
|
2024-07-30 11:47:44 +00:00
|
|
|
#include <QtCore/qbytearray.h>
|
|
|
|
#include <QtCore/qlatin1stringview.h>
|
|
|
|
|
2024-08-21 16:29:39 +00:00
|
|
|
#include <optional>
|
2022-10-20 11:49:27 +00:00
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
|
|
|
/*!
|
2024-04-19 12:18:32 +00:00
|
|
|
\class QGrpcClientBase
|
2023-03-10 09:49:15 +00:00
|
|
|
\inmodule QtGrpc
|
2024-04-19 12:18:32 +00:00
|
|
|
\brief The QGrpcClientBase class is bridge between gRPC clients
|
2022-12-08 12:53:08 +00:00
|
|
|
and channels.
|
2022-10-20 11:49:27 +00:00
|
|
|
|
2024-04-19 12:18:32 +00:00
|
|
|
QGrpcClientBase provides a set of functions for client classes
|
2022-12-08 12:53:08 +00:00
|
|
|
generated out of protobuf services.
|
2024-04-19 12:18:32 +00:00
|
|
|
QGrpcClientBase enforces thread safety for startStream() and call() methods
|
2022-12-08 12:53:08 +00:00
|
|
|
of generated clients.
|
2024-04-19 12:18:32 +00:00
|
|
|
The methods QGrpcClientBase::call() and QGrpcClientBase::startStream()
|
2023-02-21 14:34:07 +00:00
|
|
|
should only be called by the generated client classes.
|
2022-12-08 12:53:08 +00:00
|
|
|
*/
|
2022-10-20 11:49:27 +00:00
|
|
|
|
|
|
|
/*!
|
2024-07-04 14:50:07 +00:00
|
|
|
\fn template <typename StreamType, QGrpcClientBase::if_qtgrpc_stream<StreamType> = true> std::shared_ptr<StreamType> QGrpcClientBase::startStream(QLatin1StringView method, const QProtobufMessage &arg, const QGrpcCallOptions &options)
|
2022-10-20 11:49:27 +00:00
|
|
|
|
2023-09-06 10:53:08 +00:00
|
|
|
Starts the stream \a method of the \e StreamType type with the message
|
2022-12-08 12:53:08 +00:00
|
|
|
argument \a arg to the attached channel.
|
2023-08-13 16:00:42 +00:00
|
|
|
|
|
|
|
Uses \a options argument to set additional parameter in the stream
|
|
|
|
communication.
|
|
|
|
|
2023-09-06 10:53:08 +00:00
|
|
|
The implementation is only available for \e StreamType:
|
2024-08-08 14:04:36 +00:00
|
|
|
QGrpcServerStream, QGrpcClientStream, and QGrpcBidiStream.
|
2022-12-08 12:53:08 +00:00
|
|
|
*/
|
2022-10-20 11:49:27 +00:00
|
|
|
|
2024-01-14 19:03:29 +00:00
|
|
|
/*!
|
2024-04-19 12:18:32 +00:00
|
|
|
\fn void QGrpcClientBase::channelChanged()
|
2024-01-14 19:03:29 +00:00
|
|
|
\since 6.7
|
|
|
|
|
|
|
|
Indicates that a new channel is attached to the client.
|
|
|
|
*/
|
|
|
|
|
2024-01-14 19:22:07 +00:00
|
|
|
/*!
|
2024-04-19 12:18:32 +00:00
|
|
|
\fn void QGrpcClientBase::errorOccurred(const QGrpcStatus &status);
|
2024-01-14 19:22:07 +00:00
|
|
|
|
|
|
|
Indicates that an error occurred during serialization.
|
|
|
|
|
|
|
|
This signal is emitted when an error with \a status occurs in the channel
|
|
|
|
or during serialization.
|
|
|
|
*/
|
|
|
|
|
2024-04-19 12:18:32 +00:00
|
|
|
class QGrpcClientBasePrivate : public QObjectPrivate
|
2022-10-20 11:49:27 +00:00
|
|
|
{
|
2024-04-19 12:18:32 +00:00
|
|
|
Q_DECLARE_PUBLIC(QGrpcClientBase)
|
2022-10-20 11:49:27 +00:00
|
|
|
public:
|
2024-08-22 15:51:48 +00:00
|
|
|
explicit QGrpcClientBasePrivate(QLatin1StringView service) : service(service) { }
|
2022-10-20 11:49:27 +00:00
|
|
|
|
2024-05-29 14:31:05 +00:00
|
|
|
void addStream(QGrpcOperation *stream);
|
2024-08-21 16:29:39 +00:00
|
|
|
std::optional<QByteArray> trySerialize(const QProtobufMessage &arg) const;
|
2024-08-22 15:51:48 +00:00
|
|
|
bool isReady() const;
|
2024-07-04 15:12:20 +00:00
|
|
|
|
2024-08-22 15:51:48 +00:00
|
|
|
std::shared_ptr<QAbstractProtobufSerializer> serializer() const
|
|
|
|
{
|
2024-08-21 16:29:39 +00:00
|
|
|
return channel ? channel->serializer() : nullptr;
|
2024-04-02 12:05:34 +00:00
|
|
|
}
|
2023-08-06 13:42:23 +00:00
|
|
|
|
2022-12-09 13:03:47 +00:00
|
|
|
std::shared_ptr<QAbstractGrpcChannel> channel;
|
2023-09-22 14:56:02 +00:00
|
|
|
const QLatin1StringView service;
|
2024-05-29 14:31:05 +00:00
|
|
|
QMinimalFlatSet<QGrpcOperation *> activeStreams;
|
2022-10-20 11:49:27 +00:00
|
|
|
};
|
|
|
|
|
2024-05-29 14:31:05 +00:00
|
|
|
void QGrpcClientBasePrivate::addStream(QGrpcOperation *grpcStream)
|
2023-08-13 16:00:42 +00:00
|
|
|
{
|
2024-05-29 14:31:05 +00:00
|
|
|
Q_ASSERT(grpcStream);
|
|
|
|
|
2024-04-19 12:18:32 +00:00
|
|
|
Q_Q(QGrpcClientBase);
|
2024-05-29 14:31:05 +00:00
|
|
|
// Remove the operation pointer upon QObject destruction if it hasn't
|
2024-08-21 16:16:34 +00:00
|
|
|
// already been gracefully removed by receiving finished()
|
2024-05-29 14:31:05 +00:00
|
|
|
QObject::connect(grpcStream, &QObject::destroyed, q, [this, grpcStream](QObject *obj) {
|
|
|
|
Q_ASSERT(obj == grpcStream);
|
|
|
|
activeStreams.remove(grpcStream);
|
|
|
|
});
|
|
|
|
|
2023-08-13 16:00:42 +00:00
|
|
|
auto finishedConnection = std::make_shared<QMetaObject::Connection>();
|
2024-05-29 14:31:05 +00:00
|
|
|
*finishedConnection = QObject::connect(grpcStream, &QGrpcOperation::finished, q,
|
2024-08-21 16:16:34 +00:00
|
|
|
[this, grpcStream, finishedConnection] {
|
2024-05-29 14:31:05 +00:00
|
|
|
Q_ASSERT(activeStreams.contains(grpcStream));
|
|
|
|
activeStreams.remove(grpcStream);
|
2024-05-24 12:34:17 +00:00
|
|
|
QObject::disconnect(*finishedConnection);
|
|
|
|
});
|
2024-05-29 14:31:05 +00:00
|
|
|
const auto it = activeStreams.insert(grpcStream);
|
|
|
|
Q_ASSERT(it.second);
|
2023-08-13 16:00:42 +00:00
|
|
|
}
|
|
|
|
|
2024-08-21 16:29:39 +00:00
|
|
|
std::optional<QByteArray> QGrpcClientBasePrivate::trySerialize(const QProtobufMessage &arg) const
|
|
|
|
{
|
|
|
|
if (auto s = serializer())
|
|
|
|
return s->serialize(&arg);
|
|
|
|
|
|
|
|
qGrpcWarning("Serializing failed. Serializer is not ready");
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2024-08-22 15:51:48 +00:00
|
|
|
bool QGrpcClientBasePrivate::isReady() const
|
|
|
|
{
|
|
|
|
Q_Q(const QGrpcClientBase);
|
|
|
|
if (q->thread() != QThread::currentThread()) {
|
|
|
|
qGrpcWarning("QtGrpc doesn't support invocation from a different thread");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!channel) {
|
|
|
|
qGrpcWarning("No channel(s) attached");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-04-19 12:18:32 +00:00
|
|
|
QGrpcClientBase::QGrpcClientBase(QLatin1StringView service, QObject *parent)
|
|
|
|
: QObject(*new QGrpcClientBasePrivate(service), parent)
|
2022-10-20 11:49:27 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-04-19 12:18:32 +00:00
|
|
|
QGrpcClientBase::~QGrpcClientBase() = default;
|
2022-10-20 11:49:27 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
Attaches \a channel to client as transport layer for gRPC.
|
2022-12-08 12:53:08 +00:00
|
|
|
|
|
|
|
Parameters and return values will be serialized to the channel
|
|
|
|
in a format it supports.
|
2022-10-20 11:49:27 +00:00
|
|
|
|
2022-12-13 11:28:02 +00:00
|
|
|
\note \b Warning: Qt GRPC doesn't guarantee thread safety on the channel level.
|
2022-12-08 12:53:08 +00:00
|
|
|
You have to invoke the channel-related functions on the same thread as
|
2024-04-19 12:18:32 +00:00
|
|
|
QGrpcClientBase.
|
2022-10-20 11:49:27 +00:00
|
|
|
*/
|
2024-08-21 16:16:34 +00:00
|
|
|
bool QGrpcClientBase::attachChannel(std::shared_ptr<QAbstractGrpcChannel> channel)
|
2022-10-20 11:49:27 +00:00
|
|
|
{
|
2024-07-04 15:12:20 +00:00
|
|
|
Q_D(QGrpcClientBase);
|
|
|
|
// channel is not a QObject so we compare against the threadId set on construction.
|
2023-01-25 11:21:03 +00:00
|
|
|
if (channel->dPtr->threadId != QThread::currentThreadId()) {
|
2024-08-22 15:51:48 +00:00
|
|
|
qGrpcWarning("QtGrpc doesn't allow attaching the channel from a different thread");
|
2024-08-21 16:16:34 +00:00
|
|
|
return false;
|
2022-10-20 11:49:27 +00:00
|
|
|
}
|
2024-08-21 16:16:34 +00:00
|
|
|
|
2024-08-21 16:29:39 +00:00
|
|
|
for (const auto &stream : d->activeStreams) {
|
2023-08-13 16:00:42 +00:00
|
|
|
assert(stream != nullptr);
|
2023-08-06 20:07:05 +00:00
|
|
|
stream->cancel();
|
2023-08-13 16:00:42 +00:00
|
|
|
}
|
2022-12-08 12:53:08 +00:00
|
|
|
|
2024-04-26 11:31:24 +00:00
|
|
|
d->channel = std::move(channel);
|
2023-07-25 12:29:23 +00:00
|
|
|
emit channelChanged();
|
2024-08-21 16:16:34 +00:00
|
|
|
return true;
|
2023-07-25 12:29:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
2024-01-14 19:03:29 +00:00
|
|
|
\since 6.7
|
|
|
|
Returns the channel attached to this client.
|
|
|
|
*/
|
2024-04-19 12:18:32 +00:00
|
|
|
std::shared_ptr<QAbstractGrpcChannel> QGrpcClientBase::channel() const noexcept
|
2023-07-25 12:29:23 +00:00
|
|
|
{
|
2024-04-19 12:18:32 +00:00
|
|
|
Q_D(const QGrpcClientBase);
|
2023-07-25 12:29:23 +00:00
|
|
|
return d->channel;
|
2022-10-20 11:49:27 +00:00
|
|
|
}
|
|
|
|
|
2024-04-19 12:18:32 +00:00
|
|
|
std::shared_ptr<QGrpcCallReply> QGrpcClientBase::call(QLatin1StringView method,
|
2024-07-04 15:01:12 +00:00
|
|
|
const QProtobufMessage &arg,
|
|
|
|
const QGrpcCallOptions &options)
|
2022-10-20 11:49:27 +00:00
|
|
|
{
|
2024-04-19 12:18:32 +00:00
|
|
|
Q_D(QGrpcClientBase);
|
2024-08-22 15:51:48 +00:00
|
|
|
if (!d->isReady())
|
2024-07-04 15:01:12 +00:00
|
|
|
return {};
|
2023-01-02 15:06:11 +00:00
|
|
|
|
2024-08-22 15:51:48 +00:00
|
|
|
const auto argData = d->trySerialize(arg);
|
2024-07-04 15:01:12 +00:00
|
|
|
if (!argData)
|
|
|
|
return {};
|
2023-08-13 16:00:42 +00:00
|
|
|
|
2024-08-21 16:29:39 +00:00
|
|
|
return d->channel->call(method, d->service, *argData, options);
|
2022-10-20 11:49:27 +00:00
|
|
|
}
|
|
|
|
|
2023-08-06 20:53:17 +00:00
|
|
|
std::shared_ptr<QGrpcServerStream>
|
2024-07-04 15:01:12 +00:00
|
|
|
QGrpcClientBase::startServerStream(QLatin1StringView method, const QProtobufMessage &arg,
|
|
|
|
const QGrpcCallOptions &options)
|
2022-10-20 11:49:27 +00:00
|
|
|
{
|
2024-04-19 12:18:32 +00:00
|
|
|
Q_D(QGrpcClientBase);
|
2024-08-22 15:51:48 +00:00
|
|
|
if (!d->isReady())
|
2023-08-13 16:00:42 +00:00
|
|
|
return {};
|
|
|
|
|
2024-08-22 15:51:48 +00:00
|
|
|
const auto argData = d->trySerialize(arg);
|
2024-07-04 15:01:12 +00:00
|
|
|
if (!argData)
|
|
|
|
return {};
|
|
|
|
|
2024-08-22 15:51:48 +00:00
|
|
|
const auto grpcStream = d->channel->startServerStream(method, d->service, *argData, options);
|
2024-05-29 14:31:05 +00:00
|
|
|
d->addStream(grpcStream.get());
|
2023-08-13 16:00:42 +00:00
|
|
|
return grpcStream;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<QGrpcClientStream>
|
2024-07-04 15:01:12 +00:00
|
|
|
QGrpcClientBase::startClientStream(QLatin1StringView method, const QProtobufMessage &arg,
|
|
|
|
const QGrpcCallOptions &options)
|
2023-08-13 16:00:42 +00:00
|
|
|
{
|
2024-04-19 12:18:32 +00:00
|
|
|
Q_D(QGrpcClientBase);
|
2024-08-22 15:51:48 +00:00
|
|
|
if (!d->isReady())
|
2023-08-13 16:00:42 +00:00
|
|
|
return {};
|
|
|
|
|
2024-08-22 15:51:48 +00:00
|
|
|
const auto argData = d->trySerialize(arg);
|
2024-07-04 15:01:12 +00:00
|
|
|
if (!argData)
|
|
|
|
return {};
|
|
|
|
|
2024-08-22 15:51:48 +00:00
|
|
|
const auto grpcStream = d->channel->startClientStream(method, d->service, *argData, options);
|
2024-05-29 14:31:05 +00:00
|
|
|
d->addStream(grpcStream.get());
|
2023-08-13 16:00:42 +00:00
|
|
|
return grpcStream;
|
|
|
|
}
|
|
|
|
|
2024-08-08 14:04:36 +00:00
|
|
|
std::shared_ptr<QGrpcBidiStream> QGrpcClientBase::startBidiStream(QLatin1StringView method,
|
|
|
|
const QProtobufMessage &arg,
|
|
|
|
const QGrpcCallOptions &options)
|
2023-08-13 16:00:42 +00:00
|
|
|
{
|
2024-04-19 12:18:32 +00:00
|
|
|
Q_D(QGrpcClientBase);
|
2024-08-22 15:51:48 +00:00
|
|
|
if (!d->isReady())
|
2023-08-13 16:00:42 +00:00
|
|
|
return {};
|
|
|
|
|
2024-08-22 15:51:48 +00:00
|
|
|
const auto argData = d->trySerialize(arg);
|
2024-07-04 15:01:12 +00:00
|
|
|
if (!argData)
|
|
|
|
return {};
|
|
|
|
|
2024-08-22 15:51:48 +00:00
|
|
|
const auto grpcStream = d->channel->startBidiStream(method, d->service, *argData, options);
|
2024-05-29 14:31:05 +00:00
|
|
|
d->addStream(grpcStream.get());
|
2022-10-20 11:49:27 +00:00
|
|
|
return grpcStream;
|
|
|
|
}
|
|
|
|
|
2024-07-10 16:42:57 +00:00
|
|
|
bool QGrpcClientBase::event(QEvent *event)
|
|
|
|
{
|
|
|
|
return QObject::event(event);
|
|
|
|
}
|
|
|
|
|
2022-10-20 11:49:27 +00:00
|
|
|
QT_END_NAMESPACE
|
2022-12-09 12:03:04 +00:00
|
|
|
|
2024-04-19 12:18:32 +00:00
|
|
|
#include "moc_qgrpcclientbase.cpp"
|