// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMECAST_CAST_CORE_GRPC_GRPC_UNARY_CALL_H_
#define CHROMECAST_CAST_CORE_GRPC_GRPC_UNARY_CALL_H_
#include <grpcpp/grpcpp.h>
#include <grpcpp/support/client_callback.h>
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "chromecast/cast_core/grpc/grpc_call.h"
#include "chromecast/cast_core/grpc/grpc_client_reactor.h"
#include "chromecast/cast_core/grpc/grpc_status_or.h"
namespace cast {
namespace utils {
// Typedef for the unary sync method generated by gRPC compiler.
template <typename TSyncInterface, typename TRequest, typename TResponse>
using SyncUnaryMethod = grpc::Status (TSyncInterface::*)(grpc::ClientContext*,
const TRequest&,
TResponse*);
// Typedef for the unary async method generated by gRPC compiler.
template <typename TAsyncInterface, typename TRequest, typename TResponse>
using AsyncUnaryMethod = void (TAsyncInterface::*)(grpc::ClientContext*,
const TRequest*,
TResponse*,
grpc::ClientUnaryReactor*);
// A GrpcCall implementation for unary gRPC calls specialized by the
// |AsyncMethodPtr| and SyncMethodPtr function pointers. The former must always
// be specified and is used for Async calls. The latter is used for Sync calls
// and could be omitted.
// TGrpcStub - gRPC service stub type.
// TRequest - gRPC request type for a method in the stub.
// TResponse - gRPC response type for a method in the stub.
// AsyncMethodPtr - pointer to an async method in the stub that handles a
// streaming call.
// SyncMethodPtr - pointer to a sync method in the stub that handles a
// streaming call.
template <
typename TGrpcStub,
typename TRequest,
typename TResponse,
AsyncUnaryMethod<typename TGrpcStub::AsyncInterface, TRequest, TResponse>
AsyncMethodPtr,
SyncUnaryMethod<typename TGrpcStub::SyncInterface, TRequest, TResponse>
SyncMethodPtr = nullptr>
class GrpcUnaryCall : public GrpcCall<TGrpcStub, TRequest> {
public:
static_assert(AsyncMethodPtr != nullptr, "AsyncMethodPtr must be specified");
using Base = GrpcCall<TGrpcStub, TRequest>;
using Base::async;
using Base::GrpcCall;
using Base::request;
using Base::sync;
using typename Base::AsyncInterface;
using typename Base::Context;
using typename Base::Request;
using Response = TResponse;
using ResponseCallback = base::OnceCallback<void(GrpcStatusOr<Response>)>;
GrpcStatusOr<Response> Invoke() && {
static_assert(SyncMethodPtr != nullptr,
"The sync interface is not defined for the stub. Please, add "
"&StubInterface::<method_name> to the GrpcUnaryCall "
"definition in the stub class.");
Response response;
grpc::ClientContext context;
std::move(*this).options().ApplyOptionsToContext(&context);
auto status = (std::move(*this).sync()->*SyncMethodPtr)(
&context, std::move(*this).request(), &response);
if (status.ok()) {
return response;
}
return status;
}
// Invokes a unary gRPC call for a given tuple of AsyncInterface,
// AsyncMethodPtr, Request and Response. The |service| must have a |method| of
// a certain signature generated by the proto compiler.
Context InvokeAsync(ResponseCallback response_callback) && {
// Although |reactor| is a plain pointer, its ownership is transferred to
// the gRPC stack in the |Start| call.
auto reactor =
new Reactor(std::move(*this).async(), std::move(*this).request(),
std::move(*this).options(), std::move(response_callback));
reactor->Start();
return Context(reactor->context());
}
private:
using ReactorBase = GrpcClientReactor<Request, grpc::ClientUnaryReactor>;
class Reactor final : public ReactorBase {
public:
using ReactorBase::context;
using ReactorBase::request;
Reactor(AsyncInterface* async_stub,
Request request,
GrpcCallOptions options,
ResponseCallback response_callback)
: ReactorBase(std::move(request), std::move(options)),
async_interface_(async_stub),
response_callback_(std::move(response_callback)) {}
void Start() override {
ReactorBase::Start();
(async_interface_->*AsyncMethodPtr)(context(), request(), &response_,
this);
grpc::ClientUnaryReactor::StartCall();
}
private:
// Implements grpc::ClientUnaryReactor APIs.
// The method is always called on completion of all operations associated
// with this call, and deletes itself on exit.
void OnDone(const grpc::Status& status) override {
if (status.ok()) {
std::move(response_callback_).Run(std::move(response_));
} else {
std::move(response_callback_).Run(status);
}
ReactorBase::DeleteThis();
}
using AsyncStubCall = base::OnceCallback<void(grpc::ClientContext*,
const Request*,
Response*,
grpc::ClientUnaryReactor*)>;
raw_ptr<AsyncInterface> async_interface_;
ResponseCallback response_callback_;
Response response_;
};
};
} // namespace utils
} // namespace cast
#endif // CHROMECAST_CAST_CORE_GRPC_GRPC_UNARY_CALL_H_