This commit is contained in:
Yao
2024-12-20 17:49:45 +08:00
parent 86b0363ce1
commit 654d456c7d
7011 changed files with 1705926 additions and 7 deletions

273
Pods/gRPC-C++/src/cpp/client/channel_cc.cc generated Normal file
View File

@@ -0,0 +1,273 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <atomic>
#include <cstring>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <grpc/grpc.h>
#include <grpc/impl/connectivity_state.h>
#include <grpc/slice.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include <grpcpp/channel.h>
#include <grpcpp/client_context.h>
#include <grpcpp/completion_queue.h>
#include <grpcpp/impl/call.h>
#include <grpcpp/impl/call_op_set_interface.h>
#include <grpcpp/impl/completion_queue_tag.h>
#include <grpcpp/impl/rpc_method.h>
#include <grpcpp/impl/sync.h>
#include <grpcpp/support/client_interceptor.h>
#include <grpcpp/support/slice.h>
#include "src/core/lib/iomgr/iomgr.h"
namespace grpc {
Channel::Channel(
const std::string& host, grpc_channel* channel,
std::vector<
std::unique_ptr<grpc::experimental::ClientInterceptorFactoryInterface>>
interceptor_creators)
: host_(host), c_channel_(channel) {
interceptor_creators_ = std::move(interceptor_creators);
}
Channel::~Channel() {
grpc_channel_destroy(c_channel_);
CompletionQueue* callback_cq = callback_cq_.load(std::memory_order_relaxed);
if (callback_cq != nullptr) {
if (grpc_iomgr_run_in_background()) {
// gRPC-core provides the backing needed for the preferred CQ type
callback_cq->Shutdown();
} else {
CompletionQueue::ReleaseCallbackAlternativeCQ(callback_cq);
}
}
}
namespace {
inline grpc_slice SliceFromArray(const char* arr, size_t len) {
return grpc_slice_from_copied_buffer(arr, len);
}
std::string GetChannelInfoField(grpc_channel* channel,
grpc_channel_info* channel_info,
char*** channel_info_field) {
char* value = nullptr;
memset(channel_info, 0, sizeof(*channel_info));
*channel_info_field = &value;
grpc_channel_get_info(channel, channel_info);
if (value == nullptr) return "";
std::string result = value;
gpr_free(value);
return result;
}
} // namespace
std::string Channel::GetLoadBalancingPolicyName() const {
grpc_channel_info channel_info;
return GetChannelInfoField(c_channel_, &channel_info,
&channel_info.lb_policy_name);
}
std::string Channel::GetServiceConfigJSON() const {
grpc_channel_info channel_info;
return GetChannelInfoField(c_channel_, &channel_info,
&channel_info.service_config_json);
}
namespace experimental {
void ChannelResetConnectionBackoff(Channel* channel) {
grpc_channel_reset_connect_backoff(channel->c_channel_);
}
} // namespace experimental
grpc::internal::Call Channel::CreateCallInternal(
const grpc::internal::RpcMethod& method, grpc::ClientContext* context,
grpc::CompletionQueue* cq, size_t interceptor_pos) {
const bool kRegistered = method.channel_tag() && context->authority().empty();
grpc_call* c_call = nullptr;
if (kRegistered) {
c_call = grpc_channel_create_registered_call(
c_channel_, context->propagate_from_call_,
context->propagation_options_.c_bitmask(), cq->cq(),
method.channel_tag(), context->raw_deadline(), nullptr);
} else {
const ::std::string* host_str = nullptr;
if (!context->authority_.empty()) {
host_str = &context->authority_;
} else if (!host_.empty()) {
host_str = &host_;
}
grpc_slice method_slice =
SliceFromArray(method.name(), strlen(method.name()));
grpc_slice host_slice;
if (host_str != nullptr) {
host_slice = grpc::SliceFromCopiedString(*host_str);
}
c_call = grpc_channel_create_call(
c_channel_, context->propagate_from_call_,
context->propagation_options_.c_bitmask(), cq->cq(), method_slice,
host_str == nullptr ? nullptr : &host_slice, context->raw_deadline(),
nullptr);
grpc_slice_unref(method_slice);
if (host_str != nullptr) {
grpc_slice_unref(host_slice);
}
}
grpc_census_call_set_context(c_call, context->census_context());
// ClientRpcInfo should be set before call because set_call also checks
// whether the call has been cancelled, and if the call was cancelled, we
// should notify the interceptors too.
auto* info = context->set_client_rpc_info(
method.name(), method.suffix_for_stats(), method.method_type(), this,
interceptor_creators_, interceptor_pos);
context->set_call(c_call, shared_from_this());
return grpc::internal::Call(c_call, this, cq, info);
}
grpc::internal::Call Channel::CreateCall(
const grpc::internal::RpcMethod& method, grpc::ClientContext* context,
CompletionQueue* cq) {
return CreateCallInternal(method, context, cq, 0);
}
void Channel::PerformOpsOnCall(grpc::internal::CallOpSetInterface* ops,
grpc::internal::Call* call) {
ops->FillOps(
call); // Make a copy of call. It's fine since Call just has pointers
}
void* Channel::RegisterMethod(const char* method) {
return grpc_channel_register_call(
c_channel_, method, host_.empty() ? nullptr : host_.c_str(), nullptr);
}
grpc_connectivity_state Channel::GetState(bool try_to_connect) {
return grpc_channel_check_connectivity_state(c_channel_, try_to_connect);
}
namespace {
class TagSaver final : public grpc::internal::CompletionQueueTag {
public:
explicit TagSaver(void* tag) : tag_(tag) {}
~TagSaver() override {}
bool FinalizeResult(void** tag, bool* /*status*/) override {
*tag = tag_;
delete this;
return true;
}
private:
void* tag_;
};
} // namespace
void Channel::NotifyOnStateChangeImpl(grpc_connectivity_state last_observed,
gpr_timespec deadline,
grpc::CompletionQueue* cq, void* tag) {
TagSaver* tag_saver = new TagSaver(tag);
grpc_channel_watch_connectivity_state(c_channel_, last_observed, deadline,
cq->cq(), tag_saver);
}
bool Channel::WaitForStateChangeImpl(grpc_connectivity_state last_observed,
gpr_timespec deadline) {
grpc::CompletionQueue cq;
bool ok = false;
void* tag = nullptr;
NotifyOnStateChangeImpl(last_observed, deadline, &cq, nullptr);
cq.Next(&tag, &ok);
GPR_ASSERT(tag == nullptr);
return ok;
}
namespace {
class ShutdownCallback : public grpc_completion_queue_functor {
public:
ShutdownCallback() {
functor_run = &ShutdownCallback::Run;
// Set inlineable to true since this callback is trivial and thus does not
// need to be run from the executor (triggering a thread hop). This should
// only be used by internal callbacks like this and not by user application
// code.
inlineable = true;
}
// TakeCQ takes ownership of the cq into the shutdown callback
// so that the shutdown callback will be responsible for destroying it
void TakeCQ(grpc::CompletionQueue* cq) { cq_ = cq; }
// The Run function will get invoked by the completion queue library
// when the shutdown is actually complete
static void Run(grpc_completion_queue_functor* cb, int) {
auto* callback = static_cast<ShutdownCallback*>(cb);
delete callback->cq_;
delete callback;
}
private:
grpc::CompletionQueue* cq_ = nullptr;
};
} // namespace
::grpc::CompletionQueue* Channel::CallbackCQ() {
// TODO(vjpai): Consider using a single global CQ for the default CQ
// if there is no explicit per-channel CQ registered
CompletionQueue* callback_cq = callback_cq_.load(std::memory_order_acquire);
if (callback_cq != nullptr) {
return callback_cq;
}
// The callback_cq_ wasn't already set, so grab a lock and set it up exactly
// once for this channel.
grpc::internal::MutexLock l(&mu_);
callback_cq = callback_cq_.load(std::memory_order_relaxed);
if (callback_cq == nullptr) {
if (grpc_iomgr_run_in_background()) {
// gRPC-core provides the backing needed for the preferred CQ type
auto* shutdown_callback = new ShutdownCallback;
callback_cq = new grpc::CompletionQueue(grpc_completion_queue_attributes{
GRPC_CQ_CURRENT_VERSION, GRPC_CQ_CALLBACK, GRPC_CQ_DEFAULT_POLLING,
shutdown_callback});
// Transfer ownership of the new cq to its own shutdown callback
shutdown_callback->TakeCQ(callback_cq);
} else {
// Otherwise we need to use the alternative CQ variant
callback_cq = CompletionQueue::CallbackAlternativeCQ();
}
callback_cq_.store(callback_cq, std::memory_order_release);
}
return callback_cq;
}
} // namespace grpc

View File

@@ -0,0 +1,40 @@
//
// Copyright 2019 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <utility>
#include "absl/status/status.h"
#include <grpc/grpc.h>
#include <grpcpp/support/client_callback.h>
#include <grpcpp/support/status.h>
#include "src/core/lib/iomgr/closure.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/iomgr/executor.h"
#include "src/core/lib/surface/call.h"
namespace grpc {
namespace internal {
bool ClientReactor::InternalTrailersOnly(const grpc_call* call) const {
return grpc_call_is_trailers_only(call);
}
} // namespace internal
} // namespace grpc

View File

@@ -0,0 +1,189 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <stdlib.h>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/str_format.h"
#include <grpc/compression.h>
#include <grpc/grpc.h>
#include <grpc/impl/compression_types.h>
#include <grpc/status.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include <grpcpp/channel.h>
#include <grpcpp/client_context.h>
#include <grpcpp/impl/interceptor_common.h>
#include <grpcpp/impl/sync.h>
#include <grpcpp/security/credentials.h>
#include <grpcpp/server_context.h>
#include <grpcpp/support/client_interceptor.h>
#include "src/core/lib/gprpp/crash.h"
namespace grpc {
class Channel;
class DefaultGlobalClientCallbacks final
: public ClientContext::GlobalCallbacks {
public:
~DefaultGlobalClientCallbacks() override {}
void DefaultConstructor(ClientContext* /*context*/) override {}
void Destructor(ClientContext* /*context*/) override {}
};
static DefaultGlobalClientCallbacks* g_default_client_callbacks =
new DefaultGlobalClientCallbacks();
static ClientContext::GlobalCallbacks* g_client_callbacks =
g_default_client_callbacks;
ClientContext::ClientContext()
: initial_metadata_received_(false),
wait_for_ready_(false),
wait_for_ready_explicitly_set_(false),
call_(nullptr),
call_canceled_(false),
deadline_(gpr_inf_future(GPR_CLOCK_REALTIME)),
census_context_(nullptr),
propagate_from_call_(nullptr),
compression_algorithm_(GRPC_COMPRESS_NONE),
initial_metadata_corked_(false) {
g_client_callbacks->DefaultConstructor(this);
}
ClientContext::~ClientContext() {
if (call_) {
grpc_call_unref(call_);
call_ = nullptr;
}
g_client_callbacks->Destructor(this);
}
void ClientContext::set_credentials(
const std::shared_ptr<CallCredentials>& creds) {
creds_ = creds;
// If call_ is set, we have already created the call, and set the call
// credentials. This should only be done before we have started the batch
// for sending initial metadata.
if (creds_ != nullptr && call_ != nullptr) {
if (!creds_->ApplyToCall(call_)) {
SendCancelToInterceptors();
grpc_call_cancel_with_status(call_, GRPC_STATUS_CANCELLED,
"Failed to set credentials to rpc.",
nullptr);
}
}
}
std::unique_ptr<ClientContext> ClientContext::FromInternalServerContext(
const grpc::ServerContextBase& context, PropagationOptions options) {
std::unique_ptr<ClientContext> ctx(new ClientContext);
ctx->propagate_from_call_ = context.call_.call;
ctx->propagation_options_ = options;
return ctx;
}
std::unique_ptr<ClientContext> ClientContext::FromServerContext(
const grpc::ServerContextBase& server_context, PropagationOptions options) {
return FromInternalServerContext(server_context, options);
}
std::unique_ptr<ClientContext> ClientContext::FromCallbackServerContext(
const grpc::CallbackServerContext& server_context,
PropagationOptions options) {
return FromInternalServerContext(server_context, options);
}
void ClientContext::AddMetadata(const std::string& meta_key,
const std::string& meta_value) {
send_initial_metadata_.insert(std::make_pair(meta_key, meta_value));
}
void ClientContext::set_call(grpc_call* call,
const std::shared_ptr<Channel>& channel) {
internal::MutexLock lock(&mu_);
GPR_ASSERT(call_ == nullptr);
call_ = call;
channel_ = channel;
if (creds_ && !creds_->ApplyToCall(call_)) {
// TODO(yashykt): should interceptors also see this status?
SendCancelToInterceptors();
grpc_call_cancel_with_status(call, GRPC_STATUS_CANCELLED,
"Failed to set credentials to rpc.", nullptr);
}
if (call_canceled_) {
SendCancelToInterceptors();
grpc_call_cancel(call_, nullptr);
}
}
void ClientContext::set_compression_algorithm(
grpc_compression_algorithm algorithm) {
compression_algorithm_ = algorithm;
const char* algorithm_name = nullptr;
if (!grpc_compression_algorithm_name(algorithm, &algorithm_name)) {
grpc_core::Crash(absl::StrFormat(
"Name for compression algorithm '%d' unknown.", algorithm));
}
GPR_ASSERT(algorithm_name != nullptr);
AddMetadata(GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY, algorithm_name);
}
void ClientContext::TryCancel() {
internal::MutexLock lock(&mu_);
if (call_) {
SendCancelToInterceptors();
grpc_call_cancel(call_, nullptr);
} else {
call_canceled_ = true;
}
}
void ClientContext::SendCancelToInterceptors() {
internal::CancelInterceptorBatchMethods cancel_methods;
for (size_t i = 0; i < rpc_info_.interceptors_.size(); i++) {
rpc_info_.RunInterceptor(&cancel_methods, i);
}
}
std::string ClientContext::peer() const {
std::string peer;
if (call_) {
char* c_peer = grpc_call_get_peer(call_);
peer = c_peer;
gpr_free(c_peer);
}
return peer;
}
void ClientContext::SetGlobalCallbacks(GlobalCallbacks* client_callbacks) {
GPR_ASSERT(g_client_callbacks == g_default_client_callbacks);
GPR_ASSERT(client_callbacks != nullptr);
GPR_ASSERT(client_callbacks != g_default_client_callbacks);
g_client_callbacks = client_callbacks;
}
} // namespace grpc

View File

@@ -0,0 +1,47 @@
//
//
// Copyright 2018 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <grpcpp/support/client_interceptor.h>
#include "src/core/lib/gprpp/crash.h"
namespace grpc {
namespace internal {
experimental::ClientInterceptorFactoryInterface*
g_global_client_interceptor_factory = nullptr;
} // namespace internal
namespace experimental {
void RegisterGlobalClientInterceptorFactory(
ClientInterceptorFactoryInterface* factory) {
if (internal::g_global_client_interceptor_factory != nullptr) {
grpc_core::Crash(
"It is illegal to call RegisterGlobalClientInterceptorFactory "
"multiple times.");
}
internal::g_global_client_interceptor_factory = factory;
}
// For testing purposes only.
void TestOnlyResetGlobalClientInterceptorFactory() {
internal::g_global_client_interceptor_factory = nullptr;
}
} // namespace experimental
} // namespace grpc

View File

@@ -0,0 +1,40 @@
//
//
// Copyright 2023 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <grpcpp/support/client_interceptor.h>
#include "src/core/lib/gprpp/crash.h"
namespace grpc {
namespace internal {
experimental::ClientInterceptorFactoryInterface*
g_global_client_stats_interceptor_factory = nullptr;
void RegisterGlobalClientStatsInterceptorFactory(
grpc::experimental::ClientInterceptorFactoryInterface* factory) {
if (internal::g_global_client_stats_interceptor_factory != nullptr) {
grpc_core::Crash(
"It is illegal to call RegisterGlobalClientStatsInterceptorFactory "
"multiple times.");
}
internal::g_global_client_interceptor_factory = factory;
}
} // namespace internal
} // namespace grpc

View File

@@ -0,0 +1,33 @@
//
//
// Copyright 2023 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#ifndef GRPC_SRC_CPP_CLIENT_CLIENT_STATS_INTERCEPTOR_H
#define GRPC_SRC_CPP_CLIENT_CLIENT_STATS_INTERCEPTOR_H
#include <grpcpp/support/client_interceptor.h>
namespace grpc {
namespace internal {
void RegisterGlobalClientStatsInterceptorFactory(
grpc::experimental::ClientInterceptorFactoryInterface* factory);
}
} // namespace grpc
#endif // GRPC_SRC_CPP_CLIENT_CLIENT_STATS_INTERCEPTOR_H

View File

@@ -0,0 +1,92 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <grpc/grpc.h>
#include <grpc/status.h>
#include <grpcpp/channel.h>
#include <grpcpp/create_channel.h>
#include <grpcpp/impl/grpc_library.h>
#include <grpcpp/security/credentials.h>
#include <grpcpp/support/channel_arguments.h>
#include <grpcpp/support/client_interceptor.h>
#include <grpcpp/support/config.h>
#include "src/cpp/client/create_channel_internal.h"
namespace grpc {
std::shared_ptr<grpc::Channel> CreateChannel(
const grpc::string& target,
const std::shared_ptr<grpc::ChannelCredentials>& creds) {
return CreateCustomChannel(target, creds, grpc::ChannelArguments());
}
std::shared_ptr<grpc::Channel> CreateCustomChannel(
const grpc::string& target,
const std::shared_ptr<grpc::ChannelCredentials>& creds,
const grpc::ChannelArguments& args) {
grpc::internal::GrpcLibrary
init_lib; // We need to call init in case of bad creds.
return creds ? creds->CreateChannelImpl(target, args)
: grpc::CreateChannelInternal(
"",
grpc_lame_client_channel_create(
nullptr, GRPC_STATUS_INVALID_ARGUMENT,
"Invalid credentials."),
std::vector<std::unique_ptr<
grpc::experimental::
ClientInterceptorFactoryInterface>>());
}
namespace experimental {
/// Create a new \em custom \a Channel pointing to \a target with \a
/// interceptors being invoked per call.
///
/// \warning For advanced use and testing ONLY. Override default channel
/// arguments only if necessary.
///
/// \param target The URI of the endpoint to connect to.
/// \param creds Credentials to use for the created channel. If it does not
/// hold an object or is invalid, a lame channel (one on which all operations
/// fail) is returned.
/// \param args Options for channel creation.
std::shared_ptr<grpc::Channel> CreateCustomChannelWithInterceptors(
const std::string& target,
const std::shared_ptr<grpc::ChannelCredentials>& creds,
const grpc::ChannelArguments& args,
std::vector<
std::unique_ptr<grpc::experimental::ClientInterceptorFactoryInterface>>
interceptor_creators) {
grpc::internal::GrpcLibrary
init_lib; // We need to call init in case of bad creds.
return creds ? creds->CreateChannelWithInterceptors(
target, args, std::move(interceptor_creators))
: grpc::CreateChannelInternal(
"",
grpc_lame_client_channel_create(
nullptr, GRPC_STATUS_INVALID_ARGUMENT,
"Invalid credentials."),
std::move(interceptor_creators));
}
} // namespace experimental
} // namespace grpc

View File

@@ -0,0 +1,39 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include "src/cpp/client/create_channel_internal.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <grpcpp/channel.h>
namespace grpc {
std::shared_ptr<Channel> CreateChannelInternal(
const std::string& host, grpc_channel* c_channel,
std::vector<
std::unique_ptr<grpc::experimental::ClientInterceptorFactoryInterface>>
interceptor_creators) {
return std::shared_ptr<Channel>(
new Channel(host, c_channel, std::move(interceptor_creators)));
}
} // namespace grpc

View File

@@ -0,0 +1,40 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#ifndef GRPC_SRC_CPP_CLIENT_CREATE_CHANNEL_INTERNAL_H
#define GRPC_SRC_CPP_CLIENT_CREATE_CHANNEL_INTERNAL_H
#include <memory>
#include <string>
#include <vector>
#include <grpc/grpc.h>
#include <grpcpp/channel.h>
#include <grpcpp/support/client_interceptor.h>
namespace grpc {
std::shared_ptr<Channel> CreateChannelInternal(
const std::string& host, grpc_channel* c_channel,
std::vector<
std::unique_ptr<grpc::experimental::ClientInterceptorFactoryInterface>>
interceptor_creators);
} // namespace grpc
#endif // GRPC_SRC_CPP_CLIENT_CREATE_CHANNEL_INTERNAL_H

View File

@@ -0,0 +1,89 @@
//
//
// Copyright 2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <grpc/grpc.h>
#include <grpc/grpc_posix.h>
#include <grpc/grpc_security.h>
#include <grpcpp/channel.h>
#include <grpcpp/impl/grpc_library.h>
#include <grpcpp/support/channel_arguments.h>
#include <grpcpp/support/client_interceptor.h>
#include "src/cpp/client/create_channel_internal.h"
namespace grpc {
class ChannelArguments;
#ifdef GPR_SUPPORT_CHANNELS_FROM_FD
std::shared_ptr<Channel> CreateInsecureChannelFromFd(const std::string& target,
int fd) {
internal::GrpcLibrary init_lib;
grpc_channel_credentials* creds = grpc_insecure_credentials_create();
auto channel = CreateChannelInternal(
"", grpc_channel_create_from_fd(target.c_str(), fd, creds, nullptr),
std::vector<
std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
grpc_channel_credentials_release(creds);
return channel;
}
std::shared_ptr<Channel> CreateCustomInsecureChannelFromFd(
const std::string& target, int fd, const grpc::ChannelArguments& args) {
internal::GrpcLibrary init_lib;
grpc_channel_args channel_args;
args.SetChannelArgs(&channel_args);
grpc_channel_credentials* creds = grpc_insecure_credentials_create();
auto channel = CreateChannelInternal(
"", grpc_channel_create_from_fd(target.c_str(), fd, creds, &channel_args),
std::vector<
std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
grpc_channel_credentials_release(creds);
// Channel also initializes gRPC, so we can decrement the init ref count here.
return channel;
}
namespace experimental {
std::shared_ptr<Channel> CreateCustomInsecureChannelWithInterceptorsFromFd(
const std::string& target, int fd, const ChannelArguments& args,
std::vector<
std::unique_ptr<grpc::experimental::ClientInterceptorFactoryInterface>>
interceptor_creators) {
internal::GrpcLibrary init_lib;
grpc_channel_args channel_args;
args.SetChannelArgs(&channel_args);
grpc_channel_credentials* creds = grpc_insecure_credentials_create();
auto channel = CreateChannelInternal(
"", grpc_channel_create_from_fd(target.c_str(), fd, creds, &channel_args),
std::move(interceptor_creators));
grpc_channel_credentials_release(creds);
return channel;
}
} // namespace experimental
#endif // GPR_SUPPORT_CHANNELS_FROM_FD
} // namespace grpc

View File

@@ -0,0 +1,72 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpcpp/channel.h>
#include <grpcpp/security/credentials.h>
#include <grpcpp/support/channel_arguments.h>
#include <grpcpp/support/client_interceptor.h>
#include "src/cpp/client/create_channel_internal.h"
namespace grpc {
namespace {
class InsecureChannelCredentialsImpl final : public ChannelCredentials {
public:
std::shared_ptr<Channel> CreateChannelImpl(
const std::string& target, const ChannelArguments& args) override {
return CreateChannelWithInterceptors(
target, args,
std::vector<std::unique_ptr<
grpc::experimental::ClientInterceptorFactoryInterface>>());
}
std::shared_ptr<Channel> CreateChannelWithInterceptors(
const std::string& target, const ChannelArguments& args,
std::vector<std::unique_ptr<
grpc::experimental::ClientInterceptorFactoryInterface>>
interceptor_creators) override {
grpc_channel_args channel_args;
args.SetChannelArgs(&channel_args);
grpc_channel_credentials* creds = grpc_insecure_credentials_create();
std::shared_ptr<Channel> channel = grpc::CreateChannelInternal(
"", grpc_channel_create(target.c_str(), creds, &channel_args),
std::move(interceptor_creators));
grpc_channel_credentials_release(creds);
return channel;
}
SecureChannelCredentials* AsSecureCredentials() override { return nullptr; }
private:
bool IsInsecure() const override { return true; }
};
} // namespace
std::shared_ptr<ChannelCredentials> InsecureChannelCredentials() {
return std::shared_ptr<ChannelCredentials>(
new InsecureChannelCredentialsImpl());
}
} // namespace grpc

View File

@@ -0,0 +1,519 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include "src/cpp/client/secure_credentials.h"
#include <string.h>
#include <algorithm>
#include <map>
#include <utility>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_join.h"
#include "absl/types/optional.h"
#include <grpc/event_engine/event_engine.h>
#include <grpc/grpc_security_constants.h>
#include <grpc/slice.h>
#include <grpc/support/json.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <grpc/support/time.h>
#include <grpcpp/channel.h>
#include <grpcpp/impl/grpc_library.h>
#include <grpcpp/security/tls_credentials_options.h>
#include <grpcpp/support/channel_arguments.h>
#include <grpcpp/support/config.h>
#include <grpcpp/support/slice.h>
#include <grpcpp/support/status.h>
#include "src/core/lib/event_engine/default_event_engine.h"
#include "src/core/lib/gprpp/env.h"
#include "src/core/lib/gprpp/load_file.h"
#include "src/core/lib/gprpp/status_helper.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/json/json.h"
#include "src/core/lib/json/json_reader.h"
#include "src/core/lib/security/util/json_util.h"
#include "src/cpp/client/create_channel_internal.h"
#include "src/cpp/common/secure_auth_context.h"
namespace grpc {
SecureChannelCredentials::SecureChannelCredentials(
grpc_channel_credentials* c_creds)
: c_creds_(c_creds) {}
std::shared_ptr<Channel> SecureChannelCredentials::CreateChannelImpl(
const std::string& target, const ChannelArguments& args) {
return CreateChannelWithInterceptors(
target, args,
std::vector<std::unique_ptr<
grpc::experimental::ClientInterceptorFactoryInterface>>());
}
std::shared_ptr<Channel>
SecureChannelCredentials::CreateChannelWithInterceptors(
const std::string& target, const ChannelArguments& args,
std::vector<
std::unique_ptr<grpc::experimental::ClientInterceptorFactoryInterface>>
interceptor_creators) {
grpc_channel_args channel_args;
args.SetChannelArgs(&channel_args);
return grpc::CreateChannelInternal(
args.GetSslTargetNameOverride(),
grpc_channel_create(target.c_str(), c_creds_, &channel_args),
std::move(interceptor_creators));
}
SecureCallCredentials::SecureCallCredentials(grpc_call_credentials* c_creds)
: c_creds_(c_creds) {}
bool SecureCallCredentials::ApplyToCall(grpc_call* call) {
return grpc_call_set_credentials(call, c_creds_) == GRPC_CALL_OK;
}
namespace internal {
std::shared_ptr<ChannelCredentials> WrapChannelCredentials(
grpc_channel_credentials* creds) {
return creds == nullptr ? nullptr
: std::shared_ptr<ChannelCredentials>(
new SecureChannelCredentials(creds));
}
} // namespace internal
namespace {
std::shared_ptr<CallCredentials> WrapCallCredentials(
grpc_call_credentials* creds) {
return creds == nullptr ? nullptr
: std::shared_ptr<CallCredentials>(
new SecureCallCredentials(creds));
}
} // namespace
std::shared_ptr<ChannelCredentials> GoogleDefaultCredentials() {
grpc::internal::GrpcLibrary init; // To call grpc_init().
return internal::WrapChannelCredentials(
grpc_google_default_credentials_create(nullptr));
}
std::shared_ptr<CallCredentials> ExternalAccountCredentials(
const grpc::string& json_string, const std::vector<grpc::string>& scopes) {
grpc::internal::GrpcLibrary init; // To call grpc_init().
return WrapCallCredentials(grpc_external_account_credentials_create(
json_string.c_str(), absl::StrJoin(scopes, ",").c_str()));
}
// Builds SSL Credentials given SSL specific options
std::shared_ptr<ChannelCredentials> SslCredentials(
const SslCredentialsOptions& options) {
grpc::internal::GrpcLibrary init; // To call grpc_init().
grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {
options.pem_private_key.c_str(), options.pem_cert_chain.c_str()};
grpc_channel_credentials* c_creds = grpc_ssl_credentials_create(
options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(),
options.pem_private_key.empty() ? nullptr : &pem_key_cert_pair, nullptr,
nullptr);
return internal::WrapChannelCredentials(c_creds);
}
namespace experimental {
namespace {
void ClearStsCredentialsOptions(StsCredentialsOptions* options) {
if (options == nullptr) return;
options->token_exchange_service_uri.clear();
options->resource.clear();
options->audience.clear();
options->scope.clear();
options->requested_token_type.clear();
options->subject_token_path.clear();
options->subject_token_type.clear();
options->actor_token_path.clear();
options->actor_token_type.clear();
}
} // namespace
// Builds STS credentials options from JSON.
grpc::Status StsCredentialsOptionsFromJson(const std::string& json_string,
StsCredentialsOptions* options) {
if (options == nullptr) {
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"options cannot be nullptr.");
}
ClearStsCredentialsOptions(options);
auto json = grpc_core::JsonParse(json_string.c_str());
if (!json.ok() || json->type() != grpc_core::Json::Type::kObject) {
return grpc::Status(
grpc::StatusCode::INVALID_ARGUMENT,
absl::StrCat("Invalid json: ", json.status().ToString()));
}
// Required fields.
const char* value = grpc_json_get_string_property(
*json, "token_exchange_service_uri", nullptr);
if (value == nullptr) {
ClearStsCredentialsOptions(options);
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"token_exchange_service_uri must be specified.");
}
options->token_exchange_service_uri.assign(value);
value = grpc_json_get_string_property(*json, "subject_token_path", nullptr);
if (value == nullptr) {
ClearStsCredentialsOptions(options);
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"subject_token_path must be specified.");
}
options->subject_token_path.assign(value);
value = grpc_json_get_string_property(*json, "subject_token_type", nullptr);
if (value == nullptr) {
ClearStsCredentialsOptions(options);
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"subject_token_type must be specified.");
}
options->subject_token_type.assign(value);
// Optional fields.
value = grpc_json_get_string_property(*json, "resource", nullptr);
if (value != nullptr) options->resource.assign(value);
value = grpc_json_get_string_property(*json, "audience", nullptr);
if (value != nullptr) options->audience.assign(value);
value = grpc_json_get_string_property(*json, "scope", nullptr);
if (value != nullptr) options->scope.assign(value);
value = grpc_json_get_string_property(*json, "requested_token_type", nullptr);
if (value != nullptr) options->requested_token_type.assign(value);
value = grpc_json_get_string_property(*json, "actor_token_path", nullptr);
if (value != nullptr) options->actor_token_path.assign(value);
value = grpc_json_get_string_property(*json, "actor_token_type", nullptr);
if (value != nullptr) options->actor_token_type.assign(value);
return grpc::Status();
}
// Builds STS credentials Options from the $STS_CREDENTIALS env var.
grpc::Status StsCredentialsOptionsFromEnv(StsCredentialsOptions* options) {
if (options == nullptr) {
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"options cannot be nullptr.");
}
ClearStsCredentialsOptions(options);
auto sts_creds_path = grpc_core::GetEnv("STS_CREDENTIALS");
if (!sts_creds_path.has_value()) {
return grpc::Status(grpc::StatusCode::NOT_FOUND,
"STS_CREDENTIALS environment variable not set.");
}
auto json_slice =
grpc_core::LoadFile(*sts_creds_path, /*add_null_terminator=*/true);
if (!json_slice.ok()) {
return grpc::Status(grpc::StatusCode::NOT_FOUND,
json_slice.status().ToString());
}
return StsCredentialsOptionsFromJson(json_slice->as_string_view().data(),
options);
}
// C++ to Core STS Credentials options.
grpc_sts_credentials_options StsCredentialsCppToCoreOptions(
const StsCredentialsOptions& options) {
grpc_sts_credentials_options opts;
memset(&opts, 0, sizeof(opts));
opts.token_exchange_service_uri = options.token_exchange_service_uri.c_str();
opts.resource = options.resource.c_str();
opts.audience = options.audience.c_str();
opts.scope = options.scope.c_str();
opts.requested_token_type = options.requested_token_type.c_str();
opts.subject_token_path = options.subject_token_path.c_str();
opts.subject_token_type = options.subject_token_type.c_str();
opts.actor_token_path = options.actor_token_path.c_str();
opts.actor_token_type = options.actor_token_type.c_str();
return opts;
}
// Builds STS credentials.
std::shared_ptr<CallCredentials> StsCredentials(
const StsCredentialsOptions& options) {
auto opts = StsCredentialsCppToCoreOptions(options);
return WrapCallCredentials(grpc_sts_credentials_create(&opts, nullptr));
}
std::shared_ptr<CallCredentials> MetadataCredentialsFromPlugin(
std::unique_ptr<MetadataCredentialsPlugin> plugin,
grpc_security_level min_security_level) {
grpc::internal::GrpcLibrary init; // To call grpc_init().
const char* type = plugin->GetType();
grpc::MetadataCredentialsPluginWrapper* wrapper =
new grpc::MetadataCredentialsPluginWrapper(std::move(plugin));
grpc_metadata_credentials_plugin c_plugin = {
grpc::MetadataCredentialsPluginWrapper::GetMetadata,
grpc::MetadataCredentialsPluginWrapper::DebugString,
grpc::MetadataCredentialsPluginWrapper::Destroy, wrapper, type};
return WrapCallCredentials(grpc_metadata_credentials_create_from_plugin(
c_plugin, min_security_level, nullptr));
}
// Builds ALTS Credentials given ALTS specific options
std::shared_ptr<ChannelCredentials> AltsCredentials(
const AltsCredentialsOptions& options) {
grpc::internal::GrpcLibrary init; // To call grpc_init().
grpc_alts_credentials_options* c_options =
grpc_alts_credentials_client_options_create();
for (const auto& service_account : options.target_service_accounts) {
grpc_alts_credentials_client_options_add_target_service_account(
c_options, service_account.c_str());
}
grpc_channel_credentials* c_creds = grpc_alts_credentials_create(c_options);
grpc_alts_credentials_options_destroy(c_options);
return internal::WrapChannelCredentials(c_creds);
}
// Builds Local Credentials
std::shared_ptr<ChannelCredentials> LocalCredentials(
grpc_local_connect_type type) {
grpc::internal::GrpcLibrary init; // To call grpc_init().
return internal::WrapChannelCredentials(grpc_local_credentials_create(type));
}
// Builds TLS Credentials given TLS options.
std::shared_ptr<ChannelCredentials> TlsCredentials(
const TlsChannelCredentialsOptions& options) {
return internal::WrapChannelCredentials(
grpc_tls_credentials_create(options.c_credentials_options()));
}
} // namespace experimental
// Builds credentials for use when running in GCE
std::shared_ptr<CallCredentials> GoogleComputeEngineCredentials() {
grpc::internal::GrpcLibrary init; // To call grpc_init().
return WrapCallCredentials(
grpc_google_compute_engine_credentials_create(nullptr));
}
// Builds JWT credentials.
std::shared_ptr<CallCredentials> ServiceAccountJWTAccessCredentials(
const std::string& json_key, long token_lifetime_seconds) {
grpc::internal::GrpcLibrary init; // To call grpc_init().
if (token_lifetime_seconds <= 0) {
gpr_log(GPR_ERROR,
"Trying to create JWTCredentials with non-positive lifetime");
return WrapCallCredentials(nullptr);
}
gpr_timespec lifetime =
gpr_time_from_seconds(token_lifetime_seconds, GPR_TIMESPAN);
return WrapCallCredentials(grpc_service_account_jwt_access_credentials_create(
json_key.c_str(), lifetime, nullptr));
}
// Builds refresh token credentials.
std::shared_ptr<CallCredentials> GoogleRefreshTokenCredentials(
const std::string& json_refresh_token) {
grpc::internal::GrpcLibrary init; // To call grpc_init().
return WrapCallCredentials(grpc_google_refresh_token_credentials_create(
json_refresh_token.c_str(), nullptr));
}
// Builds access token credentials.
std::shared_ptr<CallCredentials> AccessTokenCredentials(
const std::string& access_token) {
grpc::internal::GrpcLibrary init; // To call grpc_init().
return WrapCallCredentials(
grpc_access_token_credentials_create(access_token.c_str(), nullptr));
}
// Builds IAM credentials.
std::shared_ptr<CallCredentials> GoogleIAMCredentials(
const std::string& authorization_token,
const std::string& authority_selector) {
grpc::internal::GrpcLibrary init; // To call grpc_init().
return WrapCallCredentials(grpc_google_iam_credentials_create(
authorization_token.c_str(), authority_selector.c_str(), nullptr));
}
// Combines one channel credentials and one call credentials into a channel
// composite credentials.
std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
const std::shared_ptr<ChannelCredentials>& channel_creds,
const std::shared_ptr<CallCredentials>& call_creds) {
// Note that we are not saving shared_ptrs to the two credentials passed in
// here. This is OK because the underlying C objects (i.e., channel_creds and
// call_creds) into grpc_composite_credentials_create will see their refcounts
// incremented.
SecureChannelCredentials* s_channel_creds =
channel_creds->AsSecureCredentials();
SecureCallCredentials* s_call_creds = call_creds->AsSecureCredentials();
if (s_channel_creds && s_call_creds) {
return internal::WrapChannelCredentials(
grpc_composite_channel_credentials_create(
s_channel_creds->GetRawCreds(), s_call_creds->GetRawCreds(),
nullptr));
}
return nullptr;
}
std::shared_ptr<CallCredentials> CompositeCallCredentials(
const std::shared_ptr<CallCredentials>& creds1,
const std::shared_ptr<CallCredentials>& creds2) {
SecureCallCredentials* s_creds1 = creds1->AsSecureCredentials();
SecureCallCredentials* s_creds2 = creds2->AsSecureCredentials();
if (s_creds1 != nullptr && s_creds2 != nullptr) {
return WrapCallCredentials(grpc_composite_call_credentials_create(
s_creds1->GetRawCreds(), s_creds2->GetRawCreds(), nullptr));
}
return nullptr;
}
std::shared_ptr<CallCredentials> MetadataCredentialsFromPlugin(
std::unique_ptr<MetadataCredentialsPlugin> plugin) {
grpc::internal::GrpcLibrary init; // To call grpc_init().
const char* type = plugin->GetType();
grpc::MetadataCredentialsPluginWrapper* wrapper =
new grpc::MetadataCredentialsPluginWrapper(std::move(plugin));
grpc_metadata_credentials_plugin c_plugin = {
grpc::MetadataCredentialsPluginWrapper::GetMetadata,
grpc::MetadataCredentialsPluginWrapper::DebugString,
grpc::MetadataCredentialsPluginWrapper::Destroy, wrapper, type};
return WrapCallCredentials(grpc_metadata_credentials_create_from_plugin(
c_plugin, GRPC_PRIVACY_AND_INTEGRITY, nullptr));
}
char* MetadataCredentialsPluginWrapper::DebugString(void* wrapper) {
GPR_ASSERT(wrapper);
MetadataCredentialsPluginWrapper* w =
static_cast<MetadataCredentialsPluginWrapper*>(wrapper);
return gpr_strdup(w->plugin_->DebugString().c_str());
}
void MetadataCredentialsPluginWrapper::Destroy(void* wrapper) {
if (wrapper == nullptr) return;
grpc_event_engine::experimental::GetDefaultEventEngine()->Run([wrapper] {
grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
grpc_core::ExecCtx exec_ctx;
delete static_cast<MetadataCredentialsPluginWrapper*>(wrapper);
});
}
int MetadataCredentialsPluginWrapper::GetMetadata(
void* wrapper, grpc_auth_metadata_context context,
grpc_credentials_plugin_metadata_cb cb, void* user_data,
grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
size_t* num_creds_md, grpc_status_code* status,
const char** error_details) {
GPR_ASSERT(wrapper);
MetadataCredentialsPluginWrapper* w =
static_cast<MetadataCredentialsPluginWrapper*>(wrapper);
if (!w->plugin_) {
*num_creds_md = 0;
*status = GRPC_STATUS_OK;
*error_details = nullptr;
return 1;
}
if (w->plugin_->IsBlocking()) {
// The internals of context may be destroyed if GetMetadata is cancelled.
// Make a copy for InvokePlugin.
grpc_auth_metadata_context context_copy = grpc_auth_metadata_context();
grpc_auth_metadata_context_copy(&context, &context_copy);
// Asynchronous return.
w->thread_pool_->Add([w, context_copy, cb, user_data]() mutable {
w->MetadataCredentialsPluginWrapper::InvokePlugin(
context_copy, cb, user_data, nullptr, nullptr, nullptr, nullptr);
grpc_auth_metadata_context_reset(&context_copy);
});
return 0;
} else {
// Synchronous return.
w->InvokePlugin(context, cb, user_data, creds_md, num_creds_md, status,
error_details);
return 1;
}
}
namespace {
void UnrefMetadata(const std::vector<grpc_metadata>& md) {
for (const auto& metadatum : md) {
grpc_slice_unref(metadatum.key);
grpc_slice_unref(metadatum.value);
}
}
} // namespace
void MetadataCredentialsPluginWrapper::InvokePlugin(
grpc_auth_metadata_context context, grpc_credentials_plugin_metadata_cb cb,
void* user_data, grpc_metadata creds_md[4], size_t* num_creds_md,
grpc_status_code* status_code, const char** error_details) {
std::multimap<std::string, std::string> metadata;
// const_cast is safe since the SecureAuthContext only inc/dec the refcount
// and the object is passed as a const ref to plugin_->GetMetadata.
SecureAuthContext cpp_channel_auth_context(
const_cast<grpc_auth_context*>(context.channel_auth_context));
Status status = plugin_->GetMetadata(context.service_url, context.method_name,
cpp_channel_auth_context, &metadata);
std::vector<grpc_metadata> md;
for (auto& metadatum : metadata) {
grpc_metadata md_entry;
md_entry.key = SliceFromCopiedString(metadatum.first);
md_entry.value = SliceFromCopiedString(metadatum.second);
md.push_back(md_entry);
}
if (creds_md != nullptr) {
// Synchronous return.
if (md.size() > GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX) {
*num_creds_md = 0;
*status_code = GRPC_STATUS_INTERNAL;
*error_details = gpr_strdup(
"blocking plugin credentials returned too many metadata keys");
UnrefMetadata(md);
} else {
for (const auto& elem : md) {
creds_md[*num_creds_md].key = elem.key;
creds_md[*num_creds_md].value = elem.value;
++(*num_creds_md);
}
*status_code = static_cast<grpc_status_code>(status.error_code());
*error_details =
status.ok() ? nullptr : gpr_strdup(status.error_message().c_str());
}
} else {
// Asynchronous return.
cb(user_data, md.empty() ? nullptr : &md[0], md.size(),
static_cast<grpc_status_code>(status.error_code()),
status.error_message().c_str());
UnrefMetadata(md);
}
}
MetadataCredentialsPluginWrapper::MetadataCredentialsPluginWrapper(
std::unique_ptr<MetadataCredentialsPlugin> plugin)
: plugin_(std::move(plugin)) {
if (plugin_->IsBlocking()) {
thread_pool_.reset(CreateDefaultThreadPool());
}
}
} // namespace grpc

View File

@@ -0,0 +1,134 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#ifndef GRPC_SRC_CPP_CLIENT_SECURE_CREDENTIALS_H
#define GRPC_SRC_CPP_CLIENT_SECURE_CREDENTIALS_H
#include <stddef.h>
#include <memory>
#include <string>
#include <vector>
#include "absl/strings/str_cat.h"
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpc/status.h>
#include <grpcpp/channel.h>
#include <grpcpp/impl/grpc_library.h>
#include <grpcpp/security/credentials.h>
#include <grpcpp/support/channel_arguments.h>
#include <grpcpp/support/client_interceptor.h>
// TODO(yashykt): We shouldn't be including "src/core" headers.
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/security/credentials/credentials.h"
#include "src/cpp/server/thread_pool_interface.h"
namespace grpc {
class Channel;
class SecureChannelCredentials final : public ChannelCredentials {
public:
explicit SecureChannelCredentials(grpc_channel_credentials* c_creds);
~SecureChannelCredentials() override {
grpc_core::ExecCtx exec_ctx;
if (c_creds_ != nullptr) c_creds_->Unref();
}
grpc_channel_credentials* GetRawCreds() { return c_creds_; }
std::shared_ptr<Channel> CreateChannelImpl(
const std::string& target, const ChannelArguments& args) override;
SecureChannelCredentials* AsSecureCredentials() override { return this; }
private:
std::shared_ptr<Channel> CreateChannelWithInterceptors(
const std::string& target, const ChannelArguments& args,
std::vector<std::unique_ptr<
grpc::experimental::ClientInterceptorFactoryInterface>>
interceptor_creators) override;
grpc_channel_credentials* const c_creds_;
};
class SecureCallCredentials final : public CallCredentials {
public:
explicit SecureCallCredentials(grpc_call_credentials* c_creds);
~SecureCallCredentials() override {
grpc_core::ExecCtx exec_ctx;
if (c_creds_ != nullptr) c_creds_->Unref();
}
grpc_call_credentials* GetRawCreds() { return c_creds_; }
bool ApplyToCall(grpc_call* call) override;
SecureCallCredentials* AsSecureCredentials() override { return this; }
std::string DebugString() override {
return absl::StrCat("SecureCallCredentials{",
std::string(c_creds_->debug_string()), "}");
}
private:
grpc_call_credentials* const c_creds_;
};
namespace internal {
std::shared_ptr<ChannelCredentials> WrapChannelCredentials(
grpc_channel_credentials* creds);
} // namespace internal
namespace experimental {
// Transforms C++ STS Credentials options to core options. The pointers of the
// resulting core options point to the memory held by the C++ options so C++
// options need to be kept alive until after the core credentials creation.
grpc_sts_credentials_options StsCredentialsCppToCoreOptions(
const StsCredentialsOptions& options);
} // namespace experimental
class MetadataCredentialsPluginWrapper final : private internal::GrpcLibrary {
public:
static void Destroy(void* wrapper);
static int GetMetadata(
void* wrapper, grpc_auth_metadata_context context,
grpc_credentials_plugin_metadata_cb cb, void* user_data,
grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
size_t* num_creds_md, grpc_status_code* status,
const char** error_details);
static char* DebugString(void* wrapper);
explicit MetadataCredentialsPluginWrapper(
std::unique_ptr<MetadataCredentialsPlugin> plugin);
private:
void InvokePlugin(
grpc_auth_metadata_context context,
grpc_credentials_plugin_metadata_cb cb, void* user_data,
grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
size_t* num_creds_md, grpc_status_code* status_code,
const char** error_details);
std::unique_ptr<ThreadPoolInterface> thread_pool_;
std::unique_ptr<MetadataCredentialsPlugin> plugin_;
};
} // namespace grpc
#endif // GRPC_SRC_CPP_CLIENT_SECURE_CREDENTIALS_H

View File

@@ -0,0 +1,54 @@
//
//
// Copyright 2020 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <memory>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpc/support/log.h>
#include <grpcpp/security/credentials.h>
#include "src/cpp/client/secure_credentials.h"
namespace grpc {
std::shared_ptr<ChannelCredentials> XdsCredentials(
const std::shared_ptr<ChannelCredentials>& fallback_creds) {
GPR_ASSERT(fallback_creds != nullptr);
if (fallback_creds->IsInsecure()) {
grpc_channel_credentials* insecure_creds =
grpc_insecure_credentials_create();
auto xds_creds = internal::WrapChannelCredentials(
grpc_xds_credentials_create(insecure_creds));
grpc_channel_credentials_release(insecure_creds);
return xds_creds;
} else {
return internal::WrapChannelCredentials(grpc_xds_credentials_create(
fallback_creds->AsSecureCredentials()->GetRawCreds()));
}
}
namespace experimental {
std::shared_ptr<ChannelCredentials> XdsCredentials(
const std::shared_ptr<ChannelCredentials>& fallback_creds) {
return grpc::XdsCredentials(fallback_creds);
}
} // namespace experimental
} // namespace grpc

180
Pods/gRPC-C++/src/cpp/common/alarm.cc generated Normal file
View File

@@ -0,0 +1,180 @@
//
// Copyright 2018 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <grpc/support/port_platform.h>
#include <atomic>
#include <functional>
#include <memory>
#include <utility>
#include "absl/status/status.h"
#include <grpc/event_engine/event_engine.h>
#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
#include <grpc/support/time.h>
#include <grpcpp/alarm.h>
#include <grpcpp/completion_queue.h>
#include <grpcpp/impl/completion_queue_tag.h>
#include "src/core/lib/event_engine/default_event_engine.h"
#include "src/core/lib/gprpp/time.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/surface/completion_queue.h"
namespace grpc {
namespace internal {
namespace {
using grpc_event_engine::experimental::EventEngine;
} // namespace
class AlarmImpl : public grpc::internal::CompletionQueueTag {
public:
AlarmImpl()
: event_engine_(grpc_event_engine::experimental::GetDefaultEventEngine()),
cq_(nullptr),
tag_(nullptr) {
gpr_ref_init(&refs_, 1);
}
~AlarmImpl() override {}
bool FinalizeResult(void** tag, bool* /*status*/) override {
*tag = tag_;
Unref();
return true;
}
void Set(grpc::CompletionQueue* cq, gpr_timespec deadline, void* tag) {
grpc_core::ExecCtx exec_ctx;
GRPC_CQ_INTERNAL_REF(cq->cq(), "alarm");
cq_ = cq->cq();
tag_ = tag;
GPR_ASSERT(grpc_cq_begin_op(cq_, this));
Ref();
GPR_ASSERT(cq_armed_.exchange(true) == false);
GPR_ASSERT(!callback_armed_.load());
cq_timer_handle_ = event_engine_->RunAfter(
grpc_core::Timestamp::FromTimespecRoundUp(deadline) -
grpc_core::ExecCtx::Get()->Now(),
[this] { OnCQAlarm(absl::OkStatus()); });
}
void Set(gpr_timespec deadline, std::function<void(bool)> f) {
grpc_core::ExecCtx exec_ctx;
// Don't use any CQ at all. Instead just use the timer to fire the function
callback_ = std::move(f);
Ref();
GPR_ASSERT(callback_armed_.exchange(true) == false);
GPR_ASSERT(!cq_armed_.load());
callback_timer_handle_ = event_engine_->RunAfter(
grpc_core::Timestamp::FromTimespecRoundUp(deadline) -
grpc_core::ExecCtx::Get()->Now(),
[this] { OnCallbackAlarm(true); });
}
void Cancel() {
grpc_core::ExecCtx exec_ctx;
if (callback_armed_.load() &&
event_engine_->Cancel(callback_timer_handle_)) {
event_engine_->Run([this] { OnCallbackAlarm(/*is_ok=*/false); });
}
if (cq_armed_.load() && event_engine_->Cancel(cq_timer_handle_)) {
event_engine_->Run(
[this] { OnCQAlarm(absl::CancelledError("cancelled")); });
}
}
void Destroy() {
Cancel();
Unref();
}
private:
void OnCQAlarm(grpc_error_handle error) {
cq_armed_.store(false);
grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
grpc_core::ExecCtx exec_ctx;
// Preserve the cq and reset the cq_ so that the alarm
// can be reset when the alarm tag is delivered.
grpc_completion_queue* cq = cq_;
cq_ = nullptr;
grpc_cq_end_op(
cq, this, error,
[](void* /*arg*/, grpc_cq_completion* /*completion*/) {}, nullptr,
&completion_);
GRPC_CQ_INTERNAL_UNREF(cq, "alarm");
}
void OnCallbackAlarm(bool is_ok) {
callback_armed_.store(false);
grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
grpc_core::ExecCtx exec_ctx;
callback_(is_ok);
Unref();
}
void Ref() { gpr_ref(&refs_); }
void Unref() {
if (gpr_unref(&refs_)) {
delete this;
}
}
std::shared_ptr<grpc_event_engine::experimental::EventEngine> event_engine_;
std::atomic<bool> cq_armed_{false};
EventEngine::TaskHandle cq_timer_handle_ = EventEngine::TaskHandle::kInvalid;
std::atomic<bool> callback_armed_{false};
EventEngine::TaskHandle callback_timer_handle_ =
EventEngine::TaskHandle::kInvalid;
gpr_refcount refs_;
grpc_cq_completion completion_;
// completion queue where events about this alarm will be posted
grpc_completion_queue* cq_;
void* tag_;
std::function<void(bool)> callback_;
};
} // namespace internal
Alarm::Alarm() : alarm_(new internal::AlarmImpl()) {}
void Alarm::SetInternal(grpc::CompletionQueue* cq, gpr_timespec deadline,
void* tag) {
// Note that we know that alarm_ is actually an internal::AlarmImpl
// but we declared it as the base pointer to avoid a forward declaration
// or exposing core data structures in the C++ public headers.
// Thus it is safe to use a static_cast to the subclass here, and the
// C++ style guide allows us to do so in this case
static_cast<internal::AlarmImpl*>(alarm_)->Set(cq, deadline, tag);
}
void Alarm::SetInternal(gpr_timespec deadline, std::function<void(bool)> f) {
// Note that we know that alarm_ is actually an internal::AlarmImpl
// but we declared it as the base pointer to avoid a forward declaration
// or exposing core data structures in the C++ public headers.
// Thus it is safe to use a static_cast to the subclass here, and the
// C++ style guide allows us to do so in this case
static_cast<internal::AlarmImpl*>(alarm_)->Set(deadline, std::move(f));
}
Alarm::~Alarm() {
if (alarm_ != nullptr) {
static_cast<internal::AlarmImpl*>(alarm_)->Destroy();
}
}
void Alarm::Cancel() { static_cast<internal::AlarmImpl*>(alarm_)->Cancel(); }
} // namespace grpc

View File

@@ -0,0 +1,72 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <utility>
#include <grpc/grpc_security.h>
#include <grpcpp/security/auth_context.h>
#include <grpcpp/support/string_ref.h>
namespace grpc {
AuthPropertyIterator::AuthPropertyIterator()
: property_(nullptr), ctx_(nullptr), index_(0), name_(nullptr) {}
AuthPropertyIterator::AuthPropertyIterator(
const grpc_auth_property* property, const grpc_auth_property_iterator* iter)
: property_(property),
ctx_(iter->ctx),
index_(iter->index),
name_(iter->name) {}
AuthPropertyIterator::~AuthPropertyIterator() {}
AuthPropertyIterator& AuthPropertyIterator::operator++() {
grpc_auth_property_iterator iter = {ctx_, index_, name_};
property_ = grpc_auth_property_iterator_next(&iter);
ctx_ = iter.ctx;
index_ = iter.index;
name_ = iter.name;
return *this;
}
AuthPropertyIterator AuthPropertyIterator::operator++(int) {
AuthPropertyIterator tmp(*this);
operator++();
return tmp;
}
bool AuthPropertyIterator::operator==(const AuthPropertyIterator& rhs) const {
if (property_ == nullptr || rhs.property_ == nullptr) {
return property_ == rhs.property_;
} else {
return index_ == rhs.index_;
}
}
bool AuthPropertyIterator::operator!=(const AuthPropertyIterator& rhs) const {
return !operator==(rhs);
}
AuthProperty AuthPropertyIterator::operator*() {
return std::pair<grpc::string_ref, grpc::string_ref>(
property_->name,
grpc::string_ref(property_->value, property_->value_length));
}
} // namespace grpc

View File

@@ -0,0 +1,220 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <algorithm>
#include <list>
#include <string>
#include <vector>
#include <grpc/impl/channel_arg_names.h>
#include <grpc/impl/compression_types.h>
#include <grpc/support/log.h>
#include <grpcpp/grpcpp.h>
#include <grpcpp/resource_quota.h>
#include <grpcpp/support/channel_arguments.h>
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/iomgr/socket_mutator.h"
namespace grpc {
ChannelArguments::ChannelArguments() {
// This will be ignored if used on the server side.
SetString(GRPC_ARG_PRIMARY_USER_AGENT_STRING, "grpc-c++/" + grpc::Version());
}
ChannelArguments::ChannelArguments(const ChannelArguments& other)
: strings_(other.strings_) {
args_.reserve(other.args_.size());
auto list_it_dst = strings_.begin();
auto list_it_src = other.strings_.begin();
for (const auto& a : other.args_) {
grpc_arg ap;
ap.type = a.type;
GPR_ASSERT(list_it_src->c_str() == a.key);
ap.key = const_cast<char*>(list_it_dst->c_str());
++list_it_src;
++list_it_dst;
switch (a.type) {
case GRPC_ARG_INTEGER:
ap.value.integer = a.value.integer;
break;
case GRPC_ARG_STRING:
GPR_ASSERT(list_it_src->c_str() == a.value.string);
ap.value.string = const_cast<char*>(list_it_dst->c_str());
++list_it_src;
++list_it_dst;
break;
case GRPC_ARG_POINTER:
ap.value.pointer = a.value.pointer;
ap.value.pointer.p = a.value.pointer.vtable->copy(ap.value.pointer.p);
break;
}
args_.push_back(ap);
}
}
ChannelArguments::~ChannelArguments() {
for (auto& arg : args_) {
if (arg.type == GRPC_ARG_POINTER) {
grpc_core::ExecCtx exec_ctx;
arg.value.pointer.vtable->destroy(arg.value.pointer.p);
}
}
}
void ChannelArguments::Swap(ChannelArguments& other) {
args_.swap(other.args_);
strings_.swap(other.strings_);
}
void ChannelArguments::SetCompressionAlgorithm(
grpc_compression_algorithm algorithm) {
SetInt(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, algorithm);
}
void ChannelArguments::SetGrpclbFallbackTimeout(int fallback_timeout) {
SetInt(GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS, fallback_timeout);
}
void ChannelArguments::SetSocketMutator(grpc_socket_mutator* mutator) {
if (!mutator) {
return;
}
grpc_arg mutator_arg = grpc_socket_mutator_to_arg(mutator);
bool replaced = false;
grpc_core::ExecCtx exec_ctx;
for (auto& arg : args_) {
if (arg.type == mutator_arg.type &&
std::string(arg.key) == std::string(mutator_arg.key)) {
GPR_ASSERT(!replaced);
arg.value.pointer.vtable->destroy(arg.value.pointer.p);
arg.value.pointer = mutator_arg.value.pointer;
replaced = true;
}
}
if (!replaced) {
strings_.push_back(std::string(mutator_arg.key));
args_.push_back(mutator_arg);
args_.back().key = const_cast<char*>(strings_.back().c_str());
}
}
// Note: a second call to this will add in front the result of the first call.
// An example is calling this on a copy of ChannelArguments which already has a
// prefix. The user can build up a prefix string by calling this multiple times,
// each with more significant identifier.
void ChannelArguments::SetUserAgentPrefix(
const std::string& user_agent_prefix) {
if (user_agent_prefix.empty()) {
return;
}
bool replaced = false;
auto strings_it = strings_.begin();
for (auto& arg : args_) {
++strings_it;
if (arg.type == GRPC_ARG_STRING) {
if (std::string(arg.key) == GRPC_ARG_PRIMARY_USER_AGENT_STRING) {
GPR_ASSERT(arg.value.string == strings_it->c_str());
*(strings_it) = user_agent_prefix + " " + arg.value.string;
arg.value.string = const_cast<char*>(strings_it->c_str());
replaced = true;
break;
}
++strings_it;
}
}
if (!replaced) {
SetString(GRPC_ARG_PRIMARY_USER_AGENT_STRING, user_agent_prefix);
}
}
void ChannelArguments::SetResourceQuota(
const grpc::ResourceQuota& resource_quota) {
SetPointerWithVtable(GRPC_ARG_RESOURCE_QUOTA,
resource_quota.c_resource_quota(),
grpc_resource_quota_arg_vtable());
}
void ChannelArguments::SetMaxReceiveMessageSize(int size) {
SetInt(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, size);
}
void ChannelArguments::SetMaxSendMessageSize(int size) {
SetInt(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH, size);
}
void ChannelArguments::SetLoadBalancingPolicyName(
const std::string& lb_policy_name) {
SetString(GRPC_ARG_LB_POLICY_NAME, lb_policy_name);
}
void ChannelArguments::SetServiceConfigJSON(
const std::string& service_config_json) {
SetString(GRPC_ARG_SERVICE_CONFIG, service_config_json);
}
void ChannelArguments::SetInt(const std::string& key, int value) {
grpc_arg arg;
arg.type = GRPC_ARG_INTEGER;
strings_.push_back(key);
arg.key = const_cast<char*>(strings_.back().c_str());
arg.value.integer = value;
args_.push_back(arg);
}
void ChannelArguments::SetPointer(const std::string& key, void* value) {
static const grpc_arg_pointer_vtable vtable = {
&PointerVtableMembers::Copy, &PointerVtableMembers::Destroy,
&PointerVtableMembers::Compare};
SetPointerWithVtable(key, value, &vtable);
}
void ChannelArguments::SetPointerWithVtable(
const std::string& key, void* value,
const grpc_arg_pointer_vtable* vtable) {
grpc_arg arg;
arg.type = GRPC_ARG_POINTER;
strings_.push_back(key);
arg.key = const_cast<char*>(strings_.back().c_str());
arg.value.pointer.p = vtable->copy(value);
arg.value.pointer.vtable = vtable;
args_.push_back(arg);
}
void ChannelArguments::SetString(const std::string& key,
const std::string& value) {
grpc_arg arg;
arg.type = GRPC_ARG_STRING;
strings_.push_back(key);
arg.key = const_cast<char*>(strings_.back().c_str());
strings_.push_back(value);
arg.value.string = const_cast<char*>(strings_.back().c_str());
args_.push_back(arg);
}
void ChannelArguments::SetChannelArgs(grpc_channel_args* channel_args) const {
channel_args->num_args = args_.size();
if (channel_args->num_args > 0) {
channel_args->args = const_cast<grpc_arg*>(&args_[0]);
}
}
} // namespace grpc

View File

@@ -0,0 +1,206 @@
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <vector>
#include "absl/base/thread_annotations.h"
#include <grpc/grpc.h>
#include <grpc/support/cpu.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
#include <grpc/support/time.h>
#include <grpcpp/completion_queue.h>
#include <grpcpp/impl/completion_queue_tag.h>
#include <grpcpp/impl/grpc_library.h>
#include "src/core/lib/gpr/useful.h"
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/gprpp/thd.h"
namespace grpc {
namespace {
gpr_once g_once_init_callback_alternative = GPR_ONCE_INIT;
grpc_core::Mutex* g_callback_alternative_mu;
// Implement a ref-counted callback CQ for global use in the alternative
// implementation so that its threads are only created once. Do this using
// explicit ref-counts and raw pointers rather than a shared-ptr since that
// has a non-trivial destructor and thus can't be used for global variables.
struct CallbackAlternativeCQ {
int refs ABSL_GUARDED_BY(g_callback_alternative_mu) = 0;
CompletionQueue* cq ABSL_GUARDED_BY(g_callback_alternative_mu);
std::vector<grpc_core::Thread>* nexting_threads
ABSL_GUARDED_BY(g_callback_alternative_mu);
CompletionQueue* Ref() {
grpc_core::MutexLock lock(&*g_callback_alternative_mu);
refs++;
if (refs == 1) {
cq = new CompletionQueue;
int num_nexting_threads =
grpc_core::Clamp(gpr_cpu_num_cores() / 2, 2u, 16u);
nexting_threads = new std::vector<grpc_core::Thread>;
for (int i = 0; i < num_nexting_threads; i++) {
nexting_threads->emplace_back(
"nexting_thread",
[](void* arg) {
grpc_completion_queue* cq =
static_cast<CompletionQueue*>(arg)->cq();
while (true) {
// Use the raw Core next function rather than the C++ Next since
// Next incorporates FinalizeResult and we actually want that
// called from the callback functor itself.
// TODO(vjpai): Migrate below to next without a timeout or idle
// phase. That's currently starving out some other polling,
// though.
auto ev = grpc_completion_queue_next(
cq,
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_millis(1000, GPR_TIMESPAN)),
nullptr);
if (ev.type == GRPC_QUEUE_SHUTDOWN) {
return;
}
if (ev.type == GRPC_QUEUE_TIMEOUT) {
gpr_sleep_until(
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_millis(100, GPR_TIMESPAN)));
continue;
}
GPR_DEBUG_ASSERT(ev.type == GRPC_OP_COMPLETE);
// We can always execute the callback inline rather than
// pushing it to another Executor thread because this
// thread is definitely running on a background thread, does not
// hold any application locks before executing the callback,
// and cannot be entered recursively.
auto* functor =
static_cast<grpc_completion_queue_functor*>(ev.tag);
functor->functor_run(functor, ev.success);
}
},
cq);
}
for (auto& th : *nexting_threads) {
th.Start();
}
}
return cq;
}
void Unref() {
grpc_core::MutexLock lock(g_callback_alternative_mu);
refs--;
if (refs == 0) {
cq->Shutdown();
for (auto& th : *nexting_threads) {
th.Join();
}
delete nexting_threads;
delete cq;
}
}
};
CallbackAlternativeCQ g_callback_alternative_cq;
} // namespace
// 'CompletionQueue' constructor can safely call GrpcLibraryCodegen(false) here
// i.e not have GrpcLibraryCodegen call grpc_init(). This is because, to create
// a 'grpc_completion_queue' instance (which is being passed as the input to
// this constructor), one must have already called grpc_init().
CompletionQueue::CompletionQueue(grpc_completion_queue* take)
: GrpcLibrary(false), cq_(take) {
InitialAvalanching();
}
void CompletionQueue::Shutdown() {
#ifndef NDEBUG
if (!ServerListEmpty()) {
gpr_log(GPR_ERROR,
"CompletionQueue shutdown being shutdown before its server.");
}
#endif
CompleteAvalanching();
}
CompletionQueue::NextStatus CompletionQueue::AsyncNextInternal(
void** tag, bool* ok, gpr_timespec deadline) {
for (;;) {
auto ev = grpc_completion_queue_next(cq_, deadline, nullptr);
switch (ev.type) {
case GRPC_QUEUE_TIMEOUT:
return TIMEOUT;
case GRPC_QUEUE_SHUTDOWN:
return SHUTDOWN;
case GRPC_OP_COMPLETE:
auto core_cq_tag =
static_cast<grpc::internal::CompletionQueueTag*>(ev.tag);
*ok = ev.success != 0;
*tag = core_cq_tag;
if (core_cq_tag->FinalizeResult(tag, ok)) {
return GOT_EVENT;
}
break;
}
}
}
CompletionQueue::CompletionQueueTLSCache::CompletionQueueTLSCache(
CompletionQueue* cq)
: cq_(cq), flushed_(false) {
grpc_completion_queue_thread_local_cache_init(cq_->cq_);
}
CompletionQueue::CompletionQueueTLSCache::~CompletionQueueTLSCache() {
GPR_ASSERT(flushed_);
}
bool CompletionQueue::CompletionQueueTLSCache::Flush(void** tag, bool* ok) {
int res = 0;
void* res_tag;
flushed_ = true;
if (grpc_completion_queue_thread_local_cache_flush(cq_->cq_, &res_tag,
&res)) {
auto core_cq_tag =
static_cast<grpc::internal::CompletionQueueTag*>(res_tag);
*ok = res == 1;
if (core_cq_tag->FinalizeResult(tag, ok)) {
return true;
}
}
return false;
}
CompletionQueue* CompletionQueue::CallbackAlternativeCQ() {
gpr_once_init(&g_once_init_callback_alternative,
[] { g_callback_alternative_mu = new grpc_core::Mutex(); });
return g_callback_alternative_cq.Ref();
}
void CompletionQueue::ReleaseCallbackAlternativeCQ(CompletionQueue* cq)
ABSL_NO_THREAD_SAFETY_ANALYSIS {
(void)cq;
// This accesses g_callback_alternative_cq without acquiring the mutex
// but it's considered safe because it just reads the pointer address.
GPR_DEBUG_ASSERT(cq == g_callback_alternative_cq.cq);
g_callback_alternative_cq.Unref();
}
} // namespace grpc

View File

@@ -0,0 +1,44 @@
//
//
// Copyright 2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <stddef.h>
#include <string>
#include <grpc/grpc.h>
#include <grpcpp/resource_quota.h>
namespace grpc {
ResourceQuota::ResourceQuota() : impl_(grpc_resource_quota_create(nullptr)) {}
ResourceQuota::ResourceQuota(const std::string& name)
: impl_(grpc_resource_quota_create(name.c_str())) {}
ResourceQuota::~ResourceQuota() { grpc_resource_quota_unref(impl_); }
ResourceQuota& ResourceQuota::Resize(size_t new_size) {
grpc_resource_quota_resize(impl_, new_size);
return *this;
}
ResourceQuota& ResourceQuota::SetMaxThreads(int new_max_threads) {
grpc_resource_quota_set_max_threads(impl_, new_max_threads);
return *this;
}
} // namespace grpc

View File

@@ -0,0 +1,21 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <grpcpp/impl/rpc_method.h>
namespace grpc {} // namespace grpc

View File

@@ -0,0 +1,99 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include "src/cpp/common/secure_auth_context.h"
#include <algorithm>
#include <grpc/grpc_security.h>
namespace grpc {
std::vector<grpc::string_ref> SecureAuthContext::GetPeerIdentity() const {
if (ctx_ == nullptr) {
return std::vector<grpc::string_ref>();
}
grpc_auth_property_iterator iter =
grpc_auth_context_peer_identity(ctx_.get());
std::vector<grpc::string_ref> identity;
const grpc_auth_property* property = nullptr;
while ((property = grpc_auth_property_iterator_next(&iter))) {
identity.push_back(
grpc::string_ref(property->value, property->value_length));
}
return identity;
}
std::string SecureAuthContext::GetPeerIdentityPropertyName() const {
if (ctx_ == nullptr) {
return "";
}
const char* name = grpc_auth_context_peer_identity_property_name(ctx_.get());
return name == nullptr ? "" : name;
}
std::vector<grpc::string_ref> SecureAuthContext::FindPropertyValues(
const std::string& name) const {
if (ctx_ == nullptr) {
return std::vector<grpc::string_ref>();
}
grpc_auth_property_iterator iter =
grpc_auth_context_find_properties_by_name(ctx_.get(), name.c_str());
const grpc_auth_property* property = nullptr;
std::vector<grpc::string_ref> values;
while ((property = grpc_auth_property_iterator_next(&iter))) {
values.push_back(grpc::string_ref(property->value, property->value_length));
}
return values;
}
AuthPropertyIterator SecureAuthContext::begin() const {
if (ctx_ != nullptr) {
grpc_auth_property_iterator iter =
grpc_auth_context_property_iterator(ctx_.get());
const grpc_auth_property* property =
grpc_auth_property_iterator_next(&iter);
return AuthPropertyIterator(property, &iter);
} else {
return end();
}
}
AuthPropertyIterator SecureAuthContext::end() const {
return AuthPropertyIterator();
}
void SecureAuthContext::AddProperty(const std::string& key,
const grpc::string_ref& value) {
if (ctx_ == nullptr) return;
grpc_auth_context_add_property(ctx_.get(), key.c_str(), value.data(),
value.size());
}
bool SecureAuthContext::SetPeerIdentityPropertyName(const std::string& name) {
if (ctx_ == nullptr) return false;
return grpc_auth_context_set_peer_identity_property_name(ctx_.get(),
name.c_str()) != 0;
}
bool SecureAuthContext::IsPeerAuthenticated() const {
if (ctx_ == nullptr) return false;
return grpc_auth_context_peer_is_authenticated(ctx_.get()) != 0;
}
} // namespace grpc

View File

@@ -0,0 +1,65 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#ifndef GRPC_SRC_CPP_COMMON_SECURE_AUTH_CONTEXT_H
#define GRPC_SRC_CPP_COMMON_SECURE_AUTH_CONTEXT_H
#include <string>
#include <vector>
#include <grpc/grpc_security.h>
#include <grpcpp/security/auth_context.h>
#include <grpcpp/support/string_ref.h>
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/security/context/security_context.h"
namespace grpc {
class SecureAuthContext final : public AuthContext {
public:
explicit SecureAuthContext(grpc_auth_context* ctx)
: ctx_(ctx != nullptr ? ctx->Ref() : nullptr) {}
~SecureAuthContext() override = default;
bool IsPeerAuthenticated() const override;
std::vector<grpc::string_ref> GetPeerIdentity() const override;
std::string GetPeerIdentityPropertyName() const override;
std::vector<grpc::string_ref> FindPropertyValues(
const std::string& name) const override;
AuthPropertyIterator begin() const override;
AuthPropertyIterator end() const override;
void AddProperty(const std::string& key,
const grpc::string_ref& value) override;
bool SetPeerIdentityPropertyName(const std::string& name) override;
private:
grpc_core::RefCountedPtr<grpc_auth_context> ctx_;
};
} // namespace grpc
#endif // GRPC_SRC_CPP_COMMON_SECURE_AUTH_CONTEXT_H

View File

@@ -0,0 +1,41 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <string>
#include <vector>
#include <grpc/grpc.h>
#include <grpc/impl/channel_arg_names.h>
#include <grpcpp/support/channel_arguments.h>
namespace grpc {
void ChannelArguments::SetSslTargetNameOverride(const std::string& name) {
SetString(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, name);
}
std::string ChannelArguments::GetSslTargetNameOverride() const {
for (unsigned int i = 0; i < args_.size(); i++) {
if (std::string(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) == args_[i].key) {
return args_[i].value.string;
}
}
return "";
}
} // namespace grpc

View File

@@ -0,0 +1,38 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <memory>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpcpp/security/auth_context.h>
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/security/context/security_context.h"
#include "src/cpp/common/secure_auth_context.h"
namespace grpc {
std::shared_ptr<const AuthContext> CreateAuthContext(grpc_call* call) {
if (call == nullptr) {
return std::shared_ptr<const AuthContext>();
}
grpc_core::RefCountedPtr<grpc_auth_context> ctx(grpc_call_auth_context(call));
return std::make_shared<SecureAuthContext>(ctx.get());
}
} // namespace grpc

View File

@@ -0,0 +1,60 @@
//
// Copyright 2020 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include <string>
#include <vector>
#include <grpc/grpc_security.h>
#include <grpc/support/log.h>
#include <grpcpp/security/tls_certificate_provider.h>
namespace grpc {
namespace experimental {
StaticDataCertificateProvider::StaticDataCertificateProvider(
const std::string& root_certificate,
const std::vector<IdentityKeyCertPair>& identity_key_cert_pairs) {
GPR_ASSERT(!root_certificate.empty() || !identity_key_cert_pairs.empty());
grpc_tls_identity_pairs* pairs_core = grpc_tls_identity_pairs_create();
for (const IdentityKeyCertPair& pair : identity_key_cert_pairs) {
grpc_tls_identity_pairs_add_pair(pairs_core, pair.private_key.c_str(),
pair.certificate_chain.c_str());
}
c_provider_ = grpc_tls_certificate_provider_static_data_create(
root_certificate.c_str(), pairs_core);
GPR_ASSERT(c_provider_ != nullptr);
};
StaticDataCertificateProvider::~StaticDataCertificateProvider() {
grpc_tls_certificate_provider_release(c_provider_);
};
FileWatcherCertificateProvider::FileWatcherCertificateProvider(
const std::string& private_key_path,
const std::string& identity_certificate_path,
const std::string& root_cert_path, unsigned int refresh_interval_sec) {
c_provider_ = grpc_tls_certificate_provider_file_watcher_create(
private_key_path.c_str(), identity_certificate_path.c_str(),
root_cert_path.c_str(), refresh_interval_sec);
GPR_ASSERT(c_provider_ != nullptr);
};
FileWatcherCertificateProvider::~FileWatcherCertificateProvider() {
grpc_tls_certificate_provider_release(c_provider_);
};
} // namespace experimental
} // namespace grpc

View File

@@ -0,0 +1,260 @@
//
// Copyright 2021 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include <stddef.h>
#include <algorithm>
#include <functional>
#include <map>
#include <utility>
#include <vector>
#include <grpc/grpc_security.h>
#include <grpc/status.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <grpcpp/impl/sync.h>
#include <grpcpp/security/tls_certificate_verifier.h>
#include <grpcpp/support/status.h>
#include <grpcpp/support/string_ref.h>
namespace grpc {
namespace experimental {
TlsCustomVerificationCheckRequest::TlsCustomVerificationCheckRequest(
grpc_tls_custom_verification_check_request* request)
: c_request_(request) {
GPR_ASSERT(c_request_ != nullptr);
}
grpc::string_ref TlsCustomVerificationCheckRequest::target_name() const {
return c_request_->target_name != nullptr ? c_request_->target_name : "";
}
grpc::string_ref TlsCustomVerificationCheckRequest::peer_cert() const {
return c_request_->peer_info.peer_cert != nullptr
? c_request_->peer_info.peer_cert
: "";
}
grpc::string_ref TlsCustomVerificationCheckRequest::peer_cert_full_chain()
const {
return c_request_->peer_info.peer_cert_full_chain != nullptr
? c_request_->peer_info.peer_cert_full_chain
: "";
}
grpc::string_ref TlsCustomVerificationCheckRequest::common_name() const {
return c_request_->peer_info.common_name != nullptr
? c_request_->peer_info.common_name
: "";
}
grpc::string_ref TlsCustomVerificationCheckRequest::verified_root_cert_subject()
const {
return c_request_->peer_info.verified_root_cert_subject != nullptr
? c_request_->peer_info.verified_root_cert_subject
: "";
}
std::vector<grpc::string_ref> TlsCustomVerificationCheckRequest::uri_names()
const {
std::vector<grpc::string_ref> uri_names;
for (size_t i = 0; i < c_request_->peer_info.san_names.uri_names_size; ++i) {
uri_names.emplace_back(c_request_->peer_info.san_names.uri_names[i]);
}
return uri_names;
}
std::vector<grpc::string_ref> TlsCustomVerificationCheckRequest::dns_names()
const {
std::vector<grpc::string_ref> dns_names;
for (size_t i = 0; i < c_request_->peer_info.san_names.dns_names_size; ++i) {
dns_names.emplace_back(c_request_->peer_info.san_names.dns_names[i]);
}
return dns_names;
}
std::vector<grpc::string_ref> TlsCustomVerificationCheckRequest::email_names()
const {
std::vector<grpc::string_ref> email_names;
for (size_t i = 0; i < c_request_->peer_info.san_names.email_names_size;
++i) {
email_names.emplace_back(c_request_->peer_info.san_names.email_names[i]);
}
return email_names;
}
std::vector<grpc::string_ref> TlsCustomVerificationCheckRequest::ip_names()
const {
std::vector<grpc::string_ref> ip_names;
for (size_t i = 0; i < c_request_->peer_info.san_names.ip_names_size; ++i) {
ip_names.emplace_back(c_request_->peer_info.san_names.ip_names[i]);
}
return ip_names;
}
CertificateVerifier::CertificateVerifier(grpc_tls_certificate_verifier* v)
: verifier_(v) {}
CertificateVerifier::~CertificateVerifier() {
grpc_tls_certificate_verifier_release(verifier_);
}
bool CertificateVerifier::Verify(TlsCustomVerificationCheckRequest* request,
std::function<void(grpc::Status)> callback,
grpc::Status* sync_status) {
GPR_ASSERT(request != nullptr);
GPR_ASSERT(request->c_request() != nullptr);
{
internal::MutexLock lock(&mu_);
request_map_.emplace(request->c_request(), std::move(callback));
}
grpc_status_code status_code = GRPC_STATUS_OK;
char* error_details = nullptr;
bool is_done = grpc_tls_certificate_verifier_verify(
verifier_, request->c_request(), &AsyncCheckDone, this, &status_code,
&error_details);
if (is_done) {
if (status_code != GRPC_STATUS_OK) {
*sync_status = grpc::Status(static_cast<grpc::StatusCode>(status_code),
error_details);
}
internal::MutexLock lock(&mu_);
request_map_.erase(request->c_request());
}
gpr_free(error_details);
return is_done;
}
void CertificateVerifier::Cancel(TlsCustomVerificationCheckRequest* request) {
GPR_ASSERT(request != nullptr);
GPR_ASSERT(request->c_request() != nullptr);
grpc_tls_certificate_verifier_cancel(verifier_, request->c_request());
}
void CertificateVerifier::AsyncCheckDone(
grpc_tls_custom_verification_check_request* request, void* callback_arg,
grpc_status_code status, const char* error_details) {
auto* self = static_cast<CertificateVerifier*>(callback_arg);
std::function<void(grpc::Status)> callback;
{
internal::MutexLock lock(&self->mu_);
auto it = self->request_map_.find(request);
if (it != self->request_map_.end()) {
callback = std::move(it->second);
self->request_map_.erase(it);
}
}
if (callback != nullptr) {
grpc::Status return_status;
if (status != GRPC_STATUS_OK) {
return_status =
grpc::Status(static_cast<grpc::StatusCode>(status), error_details);
}
callback(return_status);
}
}
ExternalCertificateVerifier::ExternalCertificateVerifier() {
base_ = new grpc_tls_certificate_verifier_external();
base_->user_data = this;
base_->verify = VerifyInCoreExternalVerifier;
base_->cancel = CancelInCoreExternalVerifier;
base_->destruct = DestructInCoreExternalVerifier;
}
ExternalCertificateVerifier::~ExternalCertificateVerifier() { delete base_; }
int ExternalCertificateVerifier::VerifyInCoreExternalVerifier(
void* user_data, grpc_tls_custom_verification_check_request* request,
grpc_tls_on_custom_verification_check_done_cb callback, void* callback_arg,
grpc_status_code* sync_status, char** sync_error_details) {
auto* self = static_cast<ExternalCertificateVerifier*>(user_data);
TlsCustomVerificationCheckRequest* cpp_request = nullptr;
{
internal::MutexLock lock(&self->mu_);
auto pair = self->request_map_.emplace(
request, AsyncRequestState(callback, callback_arg, request));
GPR_ASSERT(pair.second);
cpp_request = &pair.first->second.cpp_request;
}
grpc::Status sync_current_verifier_status;
bool is_done = self->Verify(
cpp_request,
[self, request](grpc::Status status) {
grpc_tls_on_custom_verification_check_done_cb callback = nullptr;
void* callback_arg = nullptr;
{
internal::MutexLock lock(&self->mu_);
auto it = self->request_map_.find(request);
if (it != self->request_map_.end()) {
callback = it->second.callback;
callback_arg = it->second.callback_arg;
self->request_map_.erase(it);
}
}
if (callback != nullptr) {
callback(request, callback_arg,
static_cast<grpc_status_code>(status.error_code()),
status.error_message().c_str());
}
},
&sync_current_verifier_status);
if (is_done) {
if (!sync_current_verifier_status.ok()) {
*sync_status = static_cast<grpc_status_code>(
sync_current_verifier_status.error_code());
*sync_error_details =
gpr_strdup(sync_current_verifier_status.error_message().c_str());
}
internal::MutexLock lock(&self->mu_);
self->request_map_.erase(request);
}
return is_done;
}
void ExternalCertificateVerifier::CancelInCoreExternalVerifier(
void* user_data, grpc_tls_custom_verification_check_request* request) {
auto* self = static_cast<ExternalCertificateVerifier*>(user_data);
TlsCustomVerificationCheckRequest* cpp_request = nullptr;
{
internal::MutexLock lock(&self->mu_);
auto it = self->request_map_.find(request);
if (it != self->request_map_.end()) {
cpp_request = &it->second.cpp_request;
}
}
if (cpp_request != nullptr) {
self->Cancel(cpp_request);
}
}
void ExternalCertificateVerifier::DestructInCoreExternalVerifier(
void* user_data) {
auto* self = static_cast<ExternalCertificateVerifier*>(user_data);
delete self;
}
NoOpCertificateVerifier::NoOpCertificateVerifier()
: CertificateVerifier(grpc_tls_certificate_verifier_no_op_create()) {}
HostNameCertificateVerifier::HostNameCertificateVerifier()
: CertificateVerifier(grpc_tls_certificate_verifier_host_name_create()) {}
} // namespace experimental
} // namespace grpc

View File

@@ -0,0 +1,152 @@
//
//
// Copyright 2019 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <memory>
#include <string>
#include <grpc/grpc_crl_provider.h>
#include <grpc/grpc_security.h>
#include <grpc/grpc_security_constants.h>
#include <grpc/support/log.h>
#include <grpcpp/security/tls_certificate_provider.h>
#include <grpcpp/security/tls_certificate_verifier.h>
#include <grpcpp/security/tls_credentials_options.h>
#include <grpcpp/security/tls_crl_provider.h>
namespace grpc {
namespace experimental {
TlsCredentialsOptions::TlsCredentialsOptions() {
c_credentials_options_ = grpc_tls_credentials_options_create();
}
TlsCredentialsOptions::~TlsCredentialsOptions() {
grpc_tls_credentials_options_destroy(c_credentials_options_);
}
TlsCredentialsOptions::TlsCredentialsOptions(
const TlsCredentialsOptions& other) {
c_credentials_options_ =
grpc_tls_credentials_options_copy(other.c_credentials_options_);
}
void TlsCredentialsOptions::set_certificate_provider(
std::shared_ptr<CertificateProviderInterface> certificate_provider) {
certificate_provider_ = certificate_provider;
if (certificate_provider_ != nullptr) {
grpc_tls_credentials_options_set_certificate_provider(
c_credentials_options_, certificate_provider_->c_provider());
}
}
void TlsCredentialsOptions::set_crl_provider(
std::shared_ptr<CrlProvider> crl_provider) {
grpc_tls_credentials_options_set_crl_provider(c_credentials_options_,
crl_provider);
}
void TlsCredentialsOptions::watch_root_certs() {
grpc_tls_credentials_options_watch_root_certs(c_credentials_options_);
}
void TlsCredentialsOptions::set_root_cert_name(
const std::string& root_cert_name) {
grpc_tls_credentials_options_set_root_cert_name(c_credentials_options_,
root_cert_name.c_str());
}
void TlsCredentialsOptions::watch_identity_key_cert_pairs() {
grpc_tls_credentials_options_watch_identity_key_cert_pairs(
c_credentials_options_);
}
void TlsCredentialsOptions::set_identity_cert_name(
const std::string& identity_cert_name) {
grpc_tls_credentials_options_set_identity_cert_name(
c_credentials_options_, identity_cert_name.c_str());
}
void TlsCredentialsOptions::set_crl_directory(const std::string& path) {
grpc_tls_credentials_options_set_crl_directory(c_credentials_options_,
path.c_str());
}
void TlsCredentialsOptions::set_tls_session_key_log_file_path(
const std::string& tls_session_key_log_file_path) {
grpc_tls_credentials_options_set_tls_session_key_log_file_path(
c_credentials_options_, tls_session_key_log_file_path.c_str());
}
void TlsCredentialsOptions::set_certificate_verifier(
std::shared_ptr<CertificateVerifier> certificate_verifier) {
certificate_verifier_ = certificate_verifier;
if (certificate_verifier_ != nullptr) {
grpc_tls_credentials_options_set_certificate_verifier(
c_credentials_options_, certificate_verifier_->c_verifier());
}
}
void TlsCredentialsOptions::set_min_tls_version(grpc_tls_version tls_version) {
grpc_tls_credentials_options* options = mutable_c_credentials_options();
GPR_ASSERT(options != nullptr);
grpc_tls_credentials_options_set_min_tls_version(options, tls_version);
}
void TlsCredentialsOptions::set_max_tls_version(grpc_tls_version tls_version) {
grpc_tls_credentials_options* options = mutable_c_credentials_options();
GPR_ASSERT(options != nullptr);
grpc_tls_credentials_options_set_max_tls_version(options, tls_version);
}
grpc_tls_credentials_options* TlsCredentialsOptions::c_credentials_options()
const {
return grpc_tls_credentials_options_copy(c_credentials_options_);
}
void TlsCredentialsOptions::set_check_call_host(bool check_call_host) {
grpc_tls_credentials_options* options = mutable_c_credentials_options();
GPR_ASSERT(options != nullptr);
grpc_tls_credentials_options_set_check_call_host(options, check_call_host);
}
void TlsChannelCredentialsOptions::set_verify_server_certs(
bool verify_server_certs) {
grpc_tls_credentials_options* options = mutable_c_credentials_options();
GPR_ASSERT(options != nullptr);
grpc_tls_credentials_options_set_verify_server_cert(options,
verify_server_certs);
}
void TlsServerCredentialsOptions::set_cert_request_type(
grpc_ssl_client_certificate_request_type cert_request_type) {
grpc_tls_credentials_options* options = mutable_c_credentials_options();
GPR_ASSERT(options != nullptr);
grpc_tls_credentials_options_set_cert_request_type(options,
cert_request_type);
}
void TlsServerCredentialsOptions::set_send_client_ca_list(
bool send_client_ca_list) {
grpc_tls_credentials_options* options = mutable_c_credentials_options();
GPR_ASSERT(options != nullptr);
grpc_tls_credentials_options_set_send_client_ca_list(options,
send_client_ca_list);
}
} // namespace experimental
} // namespace grpc

View File

@@ -0,0 +1,44 @@
//
//
// Copyright 2019 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <string>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include <grpc/grpc.h>
#include <grpcpp/support/validate_service_config.h>
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/service_config/service_config.h"
#include "src/core/service_config/service_config_impl.h"
namespace grpc {
namespace experimental {
std::string ValidateServiceConfigJSON(const std::string& service_config_json) {
grpc_init();
auto service_config = grpc_core::ServiceConfigImpl::Create(
grpc_core::ChannelArgs(), service_config_json.c_str());
std::string return_value;
if (!service_config.ok()) return_value = service_config.status().ToString();
grpc_shutdown();
return return_value;
}
} // namespace experimental
} // namespace grpc

View File

@@ -0,0 +1,25 @@
//
//
// Copyright 2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <string>
#include <grpcpp/grpcpp.h>
namespace grpc {
std::string Version() { return GRPC_CPP_VERSION_STRING; }
} // namespace grpc

View File

@@ -0,0 +1,33 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <grpcpp/completion_queue.h>
#include <grpcpp/generic/async_generic_service.h>
#include <grpcpp/server.h>
namespace grpc {
void AsyncGenericService::RequestCall(
GenericServerContext* ctx, GenericServerAsyncReaderWriter* reader_writer,
grpc::CompletionQueue* call_cq,
grpc::ServerCompletionQueue* notification_cq, void* tag) {
server_->RequestAsyncGenericCall(ctx, reader_writer, call_cq, notification_cq,
tag);
}
} // namespace grpc

View File

@@ -0,0 +1,421 @@
//
// Copyright 2023 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "src/cpp/server/backend_metric_recorder.h"
#include <inttypes.h>
#include <functional>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <grpc/support/log.h>
#include <grpcpp/ext/call_metric_recorder.h>
#include <grpcpp/ext/server_metric_recorder.h>
#include "src/core/lib/debug/trace.h"
#include "src/core/load_balancing/backend_metric_data.h"
using grpc_core::BackendMetricData;
namespace {
// Utilization values with soft limits must be in [0, infy).
bool IsUtilizationWithSoftLimitsValid(double util) { return util >= 0.0; }
// Other utilization values must be in [0, 1].
bool IsUtilizationValid(double utilization) {
return utilization >= 0.0 && utilization <= 1.0;
}
// Rate values (qps and eps) must be in [0, infy).
bool IsRateValid(double rate) { return rate >= 0.0; }
grpc_core::TraceFlag grpc_backend_metric_trace(false, "backend_metric");
} // namespace
namespace grpc {
namespace experimental {
std::unique_ptr<ServerMetricRecorder> ServerMetricRecorder::Create() {
return std::unique_ptr<ServerMetricRecorder>(new ServerMetricRecorder());
}
ServerMetricRecorder::ServerMetricRecorder()
: metric_state_(std::make_shared<const BackendMetricDataState>()) {}
void ServerMetricRecorder::UpdateBackendMetricDataState(
std::function<void(BackendMetricData*)> updater) {
internal::MutexLock lock(&mu_);
auto new_state = std::make_shared<BackendMetricDataState>(*metric_state_);
updater(&new_state->data);
++new_state->sequence_number;
metric_state_ = std::move(new_state);
}
void ServerMetricRecorder::SetCpuUtilization(double value) {
if (!IsUtilizationWithSoftLimitsValid(value)) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] CPU utilization rejected: %f", this, value);
}
return;
}
UpdateBackendMetricDataState(
[value](BackendMetricData* data) { data->cpu_utilization = value; });
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] CPU utilization set: %f", this, value);
}
}
void ServerMetricRecorder::SetMemoryUtilization(double value) {
if (!IsUtilizationValid(value)) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] Mem utilization rejected: %f", this, value);
}
return;
}
UpdateBackendMetricDataState(
[value](BackendMetricData* data) { data->mem_utilization = value; });
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] Mem utilization set: %f", this, value);
}
}
void ServerMetricRecorder::SetApplicationUtilization(double value) {
if (!IsUtilizationWithSoftLimitsValid(value)) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] Application utilization rejected: %f", this,
value);
}
return;
}
UpdateBackendMetricDataState([value](BackendMetricData* data) {
data->application_utilization = value;
});
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] Application utilization set: %f", this, value);
}
}
void ServerMetricRecorder::SetQps(double value) {
if (!IsRateValid(value)) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] QPS rejected: %f", this, value);
}
return;
}
UpdateBackendMetricDataState(
[value](BackendMetricData* data) { data->qps = value; });
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] QPS set: %f", this, value);
}
}
void ServerMetricRecorder::SetEps(double value) {
if (!IsRateValid(value)) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] EPS rejected: %f", this, value);
}
return;
}
UpdateBackendMetricDataState(
[value](BackendMetricData* data) { data->eps = value; });
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] EPS set: %f", this, value);
}
}
void ServerMetricRecorder::SetNamedUtilization(string_ref name, double value) {
if (!IsUtilizationValid(value)) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] Named utilization rejected: %f name: %s", this,
value, std::string(name.data(), name.size()).c_str());
}
return;
}
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] Named utilization set: %f name: %s", this, value,
std::string(name.data(), name.size()).c_str());
}
UpdateBackendMetricDataState([name, value](BackendMetricData* data) {
data->utilization[absl::string_view(name.data(), name.size())] = value;
});
}
void ServerMetricRecorder::SetAllNamedUtilization(
std::map<string_ref, double> named_utilization) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] All named utilization updated. size: %" PRIuPTR,
this, named_utilization.size());
}
UpdateBackendMetricDataState(
[utilization = std::move(named_utilization)](BackendMetricData* data) {
data->utilization.clear();
for (const auto& u : utilization) {
data->utilization[absl::string_view(u.first.data(), u.first.size())] =
u.second;
}
});
}
void ServerMetricRecorder::ClearCpuUtilization() {
UpdateBackendMetricDataState(
[](BackendMetricData* data) { data->cpu_utilization = -1; });
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] CPU utilization cleared.", this);
}
}
void ServerMetricRecorder::ClearMemoryUtilization() {
UpdateBackendMetricDataState(
[](BackendMetricData* data) { data->mem_utilization = -1; });
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] Mem utilization cleared.", this);
}
}
void ServerMetricRecorder::ClearApplicationUtilization() {
UpdateBackendMetricDataState(
[](BackendMetricData* data) { data->application_utilization = -1; });
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] Application utilization cleared.", this);
}
}
void ServerMetricRecorder::ClearQps() {
UpdateBackendMetricDataState([](BackendMetricData* data) { data->qps = -1; });
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] QPS utilization cleared.", this);
}
}
void ServerMetricRecorder::ClearEps() {
UpdateBackendMetricDataState([](BackendMetricData* data) { data->eps = -1; });
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] EPS utilization cleared.", this);
}
}
void ServerMetricRecorder::ClearNamedUtilization(string_ref name) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] Named utilization cleared. name: %s", this,
std::string(name.data(), name.size()).c_str());
}
UpdateBackendMetricDataState([name](BackendMetricData* data) {
data->utilization.erase(absl::string_view(name.data(), name.size()));
});
}
grpc_core::BackendMetricData ServerMetricRecorder::GetMetrics() const {
auto result = GetMetricsIfChanged();
return result->data;
}
std::shared_ptr<const ServerMetricRecorder::BackendMetricDataState>
ServerMetricRecorder::GetMetricsIfChanged() const {
std::shared_ptr<const BackendMetricDataState> result;
{
internal::MutexLock lock(&mu_);
result = metric_state_;
}
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
const auto& data = result->data;
gpr_log(GPR_INFO,
"[%p] GetMetrics() returned: seq:%" PRIu64
" cpu:%f mem:%f app:%f qps:%f eps:%f utilization size: %" PRIuPTR,
this, result->sequence_number, data.cpu_utilization,
data.mem_utilization, data.application_utilization, data.qps,
data.eps, data.utilization.size());
}
return result;
}
} // namespace experimental
experimental::CallMetricRecorder&
BackendMetricState::RecordCpuUtilizationMetric(double value) {
if (!IsUtilizationWithSoftLimitsValid(value)) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] CPU utilization value rejected: %f", this, value);
}
return *this;
}
cpu_utilization_.store(value, std::memory_order_relaxed);
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] CPU utilization recorded: %f", this, value);
}
return *this;
}
experimental::CallMetricRecorder&
BackendMetricState::RecordMemoryUtilizationMetric(double value) {
if (!IsUtilizationValid(value)) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] Mem utilization value rejected: %f", this, value);
}
return *this;
}
mem_utilization_.store(value, std::memory_order_relaxed);
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] Mem utilization recorded: %f", this, value);
}
return *this;
}
experimental::CallMetricRecorder&
BackendMetricState::RecordApplicationUtilizationMetric(double value) {
if (!IsUtilizationWithSoftLimitsValid(value)) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] Application utilization value rejected: %f", this,
value);
}
return *this;
}
application_utilization_.store(value, std::memory_order_relaxed);
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] Application utilization recorded: %f", this, value);
}
return *this;
}
experimental::CallMetricRecorder& BackendMetricState::RecordQpsMetric(
double value) {
if (!IsRateValid(value)) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] QPS value rejected: %f", this, value);
}
return *this;
}
qps_.store(value, std::memory_order_relaxed);
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] QPS recorded: %f", this, value);
}
return *this;
}
experimental::CallMetricRecorder& BackendMetricState::RecordEpsMetric(
double value) {
if (!IsRateValid(value)) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] EPS value rejected: %f", this, value);
}
return *this;
}
eps_.store(value, std::memory_order_relaxed);
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] EPS recorded: %f", this, value);
}
return *this;
}
experimental::CallMetricRecorder& BackendMetricState::RecordUtilizationMetric(
string_ref name, double value) {
if (!IsUtilizationValid(value)) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] Utilization value rejected: %s %f", this,
std::string(name.data(), name.length()).c_str(), value);
}
return *this;
}
internal::MutexLock lock(&mu_);
absl::string_view name_sv(name.data(), name.length());
utilization_[name_sv] = value;
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] Utilization recorded: %s %f", this,
std::string(name_sv).c_str(), value);
}
return *this;
}
experimental::CallMetricRecorder& BackendMetricState::RecordRequestCostMetric(
string_ref name, double value) {
internal::MutexLock lock(&mu_);
absl::string_view name_sv(name.data(), name.length());
request_cost_[name_sv] = value;
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] Request cost recorded: %s %f", this,
std::string(name_sv).c_str(), value);
}
return *this;
}
experimental::CallMetricRecorder& BackendMetricState::RecordNamedMetric(
string_ref name, double value) {
internal::MutexLock lock(&mu_);
absl::string_view name_sv(name.data(), name.length());
named_metrics_[name_sv] = value;
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO, "[%p] Named metric recorded: %s %f", this,
std::string(name_sv).c_str(), value);
}
return *this;
}
BackendMetricData BackendMetricState::GetBackendMetricData() {
// Merge metrics from the ServerMetricRecorder first since metrics recorded
// to CallMetricRecorder takes a higher precedence.
BackendMetricData data;
if (server_metric_recorder_ != nullptr) {
data = server_metric_recorder_->GetMetrics();
}
// Only overwrite if the value is set i.e. in the valid range.
const double cpu = cpu_utilization_.load(std::memory_order_relaxed);
if (IsUtilizationWithSoftLimitsValid(cpu)) {
data.cpu_utilization = cpu;
}
const double mem = mem_utilization_.load(std::memory_order_relaxed);
if (IsUtilizationValid(mem)) {
data.mem_utilization = mem;
}
const double app_util =
application_utilization_.load(std::memory_order_relaxed);
if (IsUtilizationWithSoftLimitsValid(app_util)) {
data.application_utilization = app_util;
}
const double qps = qps_.load(std::memory_order_relaxed);
if (IsRateValid(qps)) {
data.qps = qps;
}
const double eps = eps_.load(std::memory_order_relaxed);
if (IsRateValid(eps)) {
data.eps = eps;
}
{
internal::MutexLock lock(&mu_);
for (const auto& u : utilization_) {
data.utilization[u.first] = u.second;
}
for (const auto& r : request_cost_) {
data.request_cost[r.first] = r.second;
}
for (const auto& r : named_metrics_) {
data.named_metrics[r.first] = r.second;
}
}
if (GRPC_TRACE_FLAG_ENABLED(grpc_backend_metric_trace)) {
gpr_log(GPR_INFO,
"[%p] Backend metric data returned: cpu:%f mem:%f qps:%f eps:%f "
"utilization size:%" PRIuPTR " request_cost size:%" PRIuPTR
"named_metrics size:%" PRIuPTR,
this, data.cpu_utilization, data.mem_utilization, data.qps,
data.eps, data.utilization.size(), data.request_cost.size(),
data.named_metrics.size());
}
return data;
}
} // namespace grpc

View File

@@ -0,0 +1,89 @@
//
//
// Copyright 2023 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#ifndef GRPC_SRC_CPP_SERVER_BACKEND_METRIC_RECORDER_H
#define GRPC_SRC_CPP_SERVER_BACKEND_METRIC_RECORDER_H
#include <stdint.h>
#include <atomic>
#include <map>
#include "absl/base/thread_annotations.h"
#include "absl/strings/string_view.h"
#include <grpcpp/ext/call_metric_recorder.h>
#include <grpcpp/ext/server_metric_recorder.h>
#include <grpcpp/impl/sync.h>
#include <grpcpp/support/string_ref.h>
#include "src/core/ext/filters/backend_metrics/backend_metric_provider.h"
#include "src/core/load_balancing/backend_metric_data.h"
namespace grpc {
namespace experimental {
// Backend metrics and an associated update sequence number.
struct ServerMetricRecorder::BackendMetricDataState {
grpc_core::BackendMetricData data;
uint64_t sequence_number = 0;
};
} // namespace experimental
class BackendMetricState : public grpc_core::BackendMetricProvider,
public experimental::CallMetricRecorder {
public:
// `server_metric_recorder` is optional. When set, GetBackendMetricData()
// merges metrics from `server_metric_recorder` with metrics recorded to this.
explicit BackendMetricState(
experimental::ServerMetricRecorder* server_metric_recorder)
: server_metric_recorder_(server_metric_recorder) {}
experimental::CallMetricRecorder& RecordCpuUtilizationMetric(
double value) override;
experimental::CallMetricRecorder& RecordMemoryUtilizationMetric(
double value) override;
experimental::CallMetricRecorder& RecordApplicationUtilizationMetric(
double value) override;
experimental::CallMetricRecorder& RecordQpsMetric(double value) override;
experimental::CallMetricRecorder& RecordEpsMetric(double value) override;
experimental::CallMetricRecorder& RecordUtilizationMetric(
string_ref name, double value) override;
experimental::CallMetricRecorder& RecordRequestCostMetric(
string_ref name, double value) override;
experimental::CallMetricRecorder& RecordNamedMetric(string_ref name,
double value) override;
// This clears metrics currently recorded. Don't call twice.
grpc_core::BackendMetricData GetBackendMetricData() override;
private:
experimental::ServerMetricRecorder* server_metric_recorder_;
std::atomic<double> cpu_utilization_{-1.0};
std::atomic<double> mem_utilization_{-1.0};
std::atomic<double> application_utilization_{-1.0};
std::atomic<double> qps_{-1.0};
std::atomic<double> eps_{-1.0};
internal::Mutex mu_;
std::map<absl::string_view, double> utilization_ ABSL_GUARDED_BY(mu_);
std::map<absl::string_view, double> request_cost_ ABSL_GUARDED_BY(mu_);
std::map<absl::string_view, double> named_metrics_ ABSL_GUARDED_BY(mu_);
};
} // namespace grpc
#endif // GRPC_SRC_CPP_SERVER_BACKEND_METRIC_RECORDER_H

View File

@@ -0,0 +1,93 @@
//
//
// Copyright 2017 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <memory>
#include <string>
#include <vector>
#include <grpcpp/impl/channel_argument_option.h>
#include <grpcpp/impl/server_builder_option.h>
#include <grpcpp/impl/server_builder_plugin.h>
#include <grpcpp/support/channel_arguments.h>
namespace grpc {
std::unique_ptr<ServerBuilderOption> MakeChannelArgumentOption(
const std::string& name, const std::string& value) {
class StringOption final : public ServerBuilderOption {
public:
StringOption(const std::string& name, const std::string& value)
: name_(name), value_(value) {}
void UpdateArguments(ChannelArguments* args) override {
args->SetString(name_, value_);
}
void UpdatePlugins(
std::vector<std::unique_ptr<ServerBuilderPlugin>>* /*plugins*/)
override {}
private:
const std::string name_;
const std::string value_;
};
return std::unique_ptr<ServerBuilderOption>(new StringOption(name, value));
}
std::unique_ptr<ServerBuilderOption> MakeChannelArgumentOption(
const std::string& name, int value) {
class IntOption final : public ServerBuilderOption {
public:
IntOption(const std::string& name, int value)
: name_(name), value_(value) {}
void UpdateArguments(ChannelArguments* args) override {
args->SetInt(name_, value_);
}
void UpdatePlugins(
std::vector<std::unique_ptr<ServerBuilderPlugin>>* /*plugins*/)
override {}
private:
const std::string name_;
const int value_;
};
return std::unique_ptr<ServerBuilderOption>(new IntOption(name, value));
}
std::unique_ptr<ServerBuilderOption> MakeChannelArgumentOption(
const std::string& name, void* value) {
class PointerOption final : public ServerBuilderOption {
public:
PointerOption(const std::string& name, void* value)
: name_(name), value_(value) {}
void UpdateArguments(ChannelArguments* args) override {
args->SetPointer(name_, value_);
}
void UpdatePlugins(
std::vector<std::unique_ptr<ServerBuilderPlugin>>* /*plugins*/)
override {}
private:
const std::string name_;
void* value_;
};
return std::unique_ptr<ServerBuilderOption>(new PointerOption(name, value));
}
} // namespace grpc

View File

@@ -0,0 +1,41 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include "src/cpp/server/dynamic_thread_pool.h"
#include "src/cpp/server/thread_pool_interface.h"
#ifndef GRPC_CUSTOM_DEFAULT_THREAD_POOL
namespace grpc {
namespace {
ThreadPoolInterface* CreateDefaultThreadPoolImpl() {
return new DynamicThreadPool();
}
CreateThreadPoolFunc g_ctp_impl = CreateDefaultThreadPoolImpl;
} // namespace
ThreadPoolInterface* CreateDefaultThreadPool() { return g_ctp_impl(); }
void SetCreateThreadPool(CreateThreadPoolFunc func) { g_ctp_impl = func; }
} // namespace grpc
#endif // !GRPC_CUSTOM_DEFAULT_THREAD_POOL

View File

@@ -0,0 +1,45 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#ifndef GRPC_SRC_CPP_SERVER_DYNAMIC_THREAD_POOL_H
#define GRPC_SRC_CPP_SERVER_DYNAMIC_THREAD_POOL_H
#include <functional>
#include <memory>
#include <grpc/event_engine/event_engine.h>
#include "src/core/lib/event_engine/default_event_engine.h"
#include "src/cpp/server/thread_pool_interface.h"
namespace grpc {
class DynamicThreadPool final : public ThreadPoolInterface {
public:
void Add(const std::function<void()>& callback) override {
event_engine_->Run(callback);
}
private:
std::shared_ptr<grpc_event_engine::experimental::EventEngine> event_engine_ =
grpc_event_engine::experimental::GetDefaultEventEngine();
};
} // namespace grpc
#endif // GRPC_SRC_CPP_SERVER_DYNAMIC_THREAD_POOL_H

View File

@@ -0,0 +1,99 @@
//
//
// Copyright 2019 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include "src/cpp/server/external_connection_acceptor_impl.h"
#include <memory>
#include <utility>
#include <grpc/support/log.h>
#include <grpcpp/server_builder.h>
#include <grpcpp/support/byte_buffer.h>
#include <grpcpp/support/channel_arguments.h>
namespace grpc {
namespace internal {
namespace {
// The actual type to return to user. It co-owns the internal impl object with
// the server.
class AcceptorWrapper : public experimental::ExternalConnectionAcceptor {
public:
explicit AcceptorWrapper(std::shared_ptr<ExternalConnectionAcceptorImpl> impl)
: impl_(std::move(impl)) {}
void HandleNewConnection(NewConnectionParameters* p) override {
impl_->HandleNewConnection(p);
}
private:
std::shared_ptr<ExternalConnectionAcceptorImpl> impl_;
};
} // namespace
ExternalConnectionAcceptorImpl::ExternalConnectionAcceptorImpl(
const std::string& name,
ServerBuilder::experimental_type::ExternalConnectionType type,
std::shared_ptr<ServerCredentials> creds)
: name_(name), creds_(std::move(creds)) {
GPR_ASSERT(type ==
ServerBuilder::experimental_type::ExternalConnectionType::FROM_FD);
}
std::unique_ptr<experimental::ExternalConnectionAcceptor>
ExternalConnectionAcceptorImpl::GetAcceptor() {
grpc_core::MutexLock lock(&mu_);
GPR_ASSERT(!has_acceptor_);
has_acceptor_ = true;
return std::unique_ptr<experimental::ExternalConnectionAcceptor>(
new AcceptorWrapper(shared_from_this()));
}
void ExternalConnectionAcceptorImpl::HandleNewConnection(
experimental::ExternalConnectionAcceptor::NewConnectionParameters* p) {
grpc_core::MutexLock lock(&mu_);
if (shutdown_ || !started_) {
// TODO(yangg) clean up.
gpr_log(
GPR_ERROR,
"NOT handling external connection with fd %d, started %d, shutdown %d",
p->fd, started_, shutdown_);
return;
}
if (handler_) {
handler_->Handle(p->listener_fd, p->fd, p->read_buffer.c_buffer());
}
}
void ExternalConnectionAcceptorImpl::Shutdown() {
grpc_core::MutexLock lock(&mu_);
shutdown_ = true;
}
void ExternalConnectionAcceptorImpl::Start() {
grpc_core::MutexLock lock(&mu_);
GPR_ASSERT(!started_);
GPR_ASSERT(has_acceptor_);
GPR_ASSERT(!shutdown_);
started_ = true;
}
void ExternalConnectionAcceptorImpl::SetToChannelArgs(ChannelArguments* args) {
args->SetPointer(name_, &handler_);
}
} // namespace internal
} // namespace grpc

View File

@@ -0,0 +1,71 @@
//
//
// Copyright 2019 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#ifndef GRPC_SRC_CPP_SERVER_EXTERNAL_CONNECTION_ACCEPTOR_IMPL_H
#define GRPC_SRC_CPP_SERVER_EXTERNAL_CONNECTION_ACCEPTOR_IMPL_H
#include <memory>
#include <string>
#include <grpcpp/security/server_credentials.h>
#include <grpcpp/server_builder.h>
#include <grpcpp/support/channel_arguments.h>
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/iomgr/tcp_server.h"
namespace grpc {
namespace internal {
class ExternalConnectionAcceptorImpl
: public std::enable_shared_from_this<ExternalConnectionAcceptorImpl> {
public:
ExternalConnectionAcceptorImpl(
const std::string& name,
ServerBuilder::experimental_type::ExternalConnectionType type,
std::shared_ptr<ServerCredentials> creds);
// Should only be called once.
std::unique_ptr<experimental::ExternalConnectionAcceptor> GetAcceptor();
void HandleNewConnection(
experimental::ExternalConnectionAcceptor::NewConnectionParameters* p);
void Shutdown();
void Start();
const char* name() { return name_.c_str(); }
ServerCredentials* GetCredentials() { return creds_.get(); }
void SetToChannelArgs(grpc::ChannelArguments* args);
private:
const std::string name_;
std::shared_ptr<ServerCredentials> creds_;
grpc_core::TcpServerFdHandler* handler_ = nullptr; // not owned
grpc_core::Mutex mu_;
bool has_acceptor_ = false;
bool started_ = false;
bool shutdown_ = false;
};
} // namespace internal
} // namespace grpc
#endif // GRPC_SRC_CPP_SERVER_EXTERNAL_CONNECTION_ACCEPTOR_IMPL_H

View File

@@ -0,0 +1,370 @@
//
//
// Copyright 2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include "src/cpp/server/health/default_health_check_service.h"
#include <stdint.h>
#include <memory>
#include <utility>
#include "upb/base/string_view.h"
#include "upb/mem/arena.hpp"
#include <grpc/slice.h>
#include <grpc/support/log.h>
#include <grpcpp/impl/rpc_method.h>
#include <grpcpp/impl/rpc_service_method.h>
#include <grpcpp/impl/server_callback_handlers.h>
#include <grpcpp/support/slice.h>
#include "src/proto/grpc/health/v1/health.upb.h"
#define MAX_SERVICE_NAME_LENGTH 200
namespace grpc {
//
// DefaultHealthCheckService
//
DefaultHealthCheckService::DefaultHealthCheckService() {
services_map_[""].SetServingStatus(SERVING);
}
void DefaultHealthCheckService::SetServingStatus(
const std::string& service_name, bool serving) {
grpc::internal::MutexLock lock(&mu_);
if (shutdown_) {
// Set to NOT_SERVING in case service_name is not in the map.
serving = false;
}
services_map_[service_name].SetServingStatus(serving ? SERVING : NOT_SERVING);
}
void DefaultHealthCheckService::SetServingStatus(bool serving) {
const ServingStatus status = serving ? SERVING : NOT_SERVING;
grpc::internal::MutexLock lock(&mu_);
if (shutdown_) return;
for (auto& p : services_map_) {
ServiceData& service_data = p.second;
service_data.SetServingStatus(status);
}
}
void DefaultHealthCheckService::Shutdown() {
grpc::internal::MutexLock lock(&mu_);
if (shutdown_) return;
shutdown_ = true;
for (auto& p : services_map_) {
ServiceData& service_data = p.second;
service_data.SetServingStatus(NOT_SERVING);
}
}
DefaultHealthCheckService::ServingStatus
DefaultHealthCheckService::GetServingStatus(
const std::string& service_name) const {
grpc::internal::MutexLock lock(&mu_);
auto it = services_map_.find(service_name);
if (it == services_map_.end()) return NOT_FOUND;
const ServiceData& service_data = it->second;
return service_data.GetServingStatus();
}
void DefaultHealthCheckService::RegisterWatch(
const std::string& service_name,
grpc_core::RefCountedPtr<HealthCheckServiceImpl::WatchReactor> watcher) {
grpc::internal::MutexLock lock(&mu_);
ServiceData& service_data = services_map_[service_name];
watcher->SendHealth(service_data.GetServingStatus());
service_data.AddWatch(std::move(watcher));
}
void DefaultHealthCheckService::UnregisterWatch(
const std::string& service_name,
HealthCheckServiceImpl::WatchReactor* watcher) {
grpc::internal::MutexLock lock(&mu_);
auto it = services_map_.find(service_name);
if (it == services_map_.end()) return;
ServiceData& service_data = it->second;
service_data.RemoveWatch(watcher);
if (service_data.Unused()) services_map_.erase(it);
}
DefaultHealthCheckService::HealthCheckServiceImpl*
DefaultHealthCheckService::GetHealthCheckService() {
GPR_ASSERT(impl_ == nullptr);
impl_ = std::make_unique<HealthCheckServiceImpl>(this);
return impl_.get();
}
//
// DefaultHealthCheckService::ServiceData
//
void DefaultHealthCheckService::ServiceData::SetServingStatus(
ServingStatus status) {
status_ = status;
for (const auto& p : watchers_) {
p.first->SendHealth(status);
}
}
void DefaultHealthCheckService::ServiceData::AddWatch(
grpc_core::RefCountedPtr<HealthCheckServiceImpl::WatchReactor> watcher) {
watchers_[watcher.get()] = std::move(watcher);
}
void DefaultHealthCheckService::ServiceData::RemoveWatch(
HealthCheckServiceImpl::WatchReactor* watcher) {
watchers_.erase(watcher);
}
//
// DefaultHealthCheckService::HealthCheckServiceImpl
//
namespace {
const char kHealthCheckMethodName[] = "/grpc.health.v1.Health/Check";
const char kHealthWatchMethodName[] = "/grpc.health.v1.Health/Watch";
} // namespace
DefaultHealthCheckService::HealthCheckServiceImpl::HealthCheckServiceImpl(
DefaultHealthCheckService* database)
: database_(database) {
// Add Check() method.
AddMethod(new internal::RpcServiceMethod(
kHealthCheckMethodName, internal::RpcMethod::NORMAL_RPC, nullptr));
MarkMethodCallback(
0, new internal::CallbackUnaryHandler<ByteBuffer, ByteBuffer>(
[database](CallbackServerContext* context,
const ByteBuffer* request, ByteBuffer* response) {
return HandleCheckRequest(database, context, request, response);
}));
// Add Watch() method.
AddMethod(new internal::RpcServiceMethod(
kHealthWatchMethodName, internal::RpcMethod::SERVER_STREAMING, nullptr));
MarkMethodCallback(
1, new internal::CallbackServerStreamingHandler<ByteBuffer, ByteBuffer>(
[this](CallbackServerContext* /*ctx*/, const ByteBuffer* request) {
return new WatchReactor(this, request);
}));
}
DefaultHealthCheckService::HealthCheckServiceImpl::~HealthCheckServiceImpl() {
grpc::internal::MutexLock lock(&mu_);
shutdown_ = true;
while (num_watches_ > 0) {
shutdown_condition_.Wait(&mu_);
}
}
ServerUnaryReactor*
DefaultHealthCheckService::HealthCheckServiceImpl::HandleCheckRequest(
DefaultHealthCheckService* database, CallbackServerContext* context,
const ByteBuffer* request, ByteBuffer* response) {
auto* reactor = context->DefaultReactor();
std::string service_name;
if (!DecodeRequest(*request, &service_name)) {
reactor->Finish(
Status(StatusCode::INVALID_ARGUMENT, "could not parse request"));
return reactor;
}
ServingStatus serving_status = database->GetServingStatus(service_name);
if (serving_status == NOT_FOUND) {
reactor->Finish(Status(StatusCode::NOT_FOUND, "service name unknown"));
return reactor;
}
if (!EncodeResponse(serving_status, response)) {
reactor->Finish(Status(StatusCode::INTERNAL, "could not encode response"));
return reactor;
}
reactor->Finish(Status::OK);
return reactor;
}
bool DefaultHealthCheckService::HealthCheckServiceImpl::DecodeRequest(
const ByteBuffer& request, std::string* service_name) {
Slice slice;
if (!request.DumpToSingleSlice(&slice).ok()) return false;
uint8_t* request_bytes = nullptr;
size_t request_size = 0;
request_bytes = const_cast<uint8_t*>(slice.begin());
request_size = slice.size();
upb::Arena arena;
grpc_health_v1_HealthCheckRequest* request_struct =
grpc_health_v1_HealthCheckRequest_parse(
reinterpret_cast<char*>(request_bytes), request_size, arena.ptr());
if (request_struct == nullptr) {
return false;
}
upb_StringView service =
grpc_health_v1_HealthCheckRequest_service(request_struct);
if (service.size > MAX_SERVICE_NAME_LENGTH) {
return false;
}
service_name->assign(service.data, service.size);
return true;
}
bool DefaultHealthCheckService::HealthCheckServiceImpl::EncodeResponse(
ServingStatus status, ByteBuffer* response) {
upb::Arena arena;
grpc_health_v1_HealthCheckResponse* response_struct =
grpc_health_v1_HealthCheckResponse_new(arena.ptr());
grpc_health_v1_HealthCheckResponse_set_status(
response_struct,
status == NOT_FOUND ? grpc_health_v1_HealthCheckResponse_SERVICE_UNKNOWN
: status == SERVING ? grpc_health_v1_HealthCheckResponse_SERVING
: grpc_health_v1_HealthCheckResponse_NOT_SERVING);
size_t buf_length;
char* buf = grpc_health_v1_HealthCheckResponse_serialize(
response_struct, arena.ptr(), &buf_length);
if (buf == nullptr) {
return false;
}
grpc_slice response_slice = grpc_slice_from_copied_buffer(buf, buf_length);
Slice encoded_response(response_slice, Slice::STEAL_REF);
ByteBuffer response_buffer(&encoded_response, 1);
response->Swap(&response_buffer);
return true;
}
//
// DefaultHealthCheckService::HealthCheckServiceImpl::WatchReactor
//
DefaultHealthCheckService::HealthCheckServiceImpl::WatchReactor::WatchReactor(
HealthCheckServiceImpl* service, const ByteBuffer* request)
: service_(service) {
{
grpc::internal::MutexLock lock(&service_->mu_);
++service_->num_watches_;
}
bool success = DecodeRequest(*request, &service_name_);
gpr_log(GPR_DEBUG, "[HCS %p] watcher %p \"%s\": watch call started", service_,
this, service_name_.c_str());
if (!success) {
MaybeFinishLocked(Status(StatusCode::INTERNAL, "could not parse request"));
return;
}
// Register the call for updates to the service.
service_->database_->RegisterWatch(service_name_, Ref());
}
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchReactor::
SendHealth(ServingStatus status) {
gpr_log(GPR_DEBUG,
"[HCS %p] watcher %p \"%s\": SendHealth() for ServingStatus %d",
service_, this, service_name_.c_str(), status);
grpc::internal::MutexLock lock(&mu_);
// If there's already a send in flight, cache the new status, and
// we'll start a new send for it when the one in flight completes.
if (write_pending_) {
gpr_log(GPR_DEBUG, "[HCS %p] watcher %p \"%s\": queuing write", service_,
this, service_name_.c_str());
pending_status_ = status;
return;
}
// Start a send.
SendHealthLocked(status);
}
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchReactor::
SendHealthLocked(ServingStatus status) {
// Do nothing if Finish() has already been called.
if (finish_called_) return;
// Check if we're shutting down.
{
grpc::internal::MutexLock lock(&service_->mu_);
if (service_->shutdown_) {
MaybeFinishLocked(
Status(StatusCode::CANCELLED, "not writing due to shutdown"));
return;
}
}
// Send response.
bool success = EncodeResponse(status, &response_);
if (!success) {
MaybeFinishLocked(
Status(StatusCode::INTERNAL, "could not encode response"));
return;
}
gpr_log(GPR_DEBUG,
"[HCS %p] watcher %p \"%s\": starting write for ServingStatus %d",
service_, this, service_name_.c_str(), status);
write_pending_ = true;
StartWrite(&response_);
}
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchReactor::
OnWriteDone(bool ok) {
gpr_log(GPR_DEBUG, "[HCS %p] watcher %p \"%s\": OnWriteDone(): ok=%d",
service_, this, service_name_.c_str(), ok);
response_.Clear();
grpc::internal::MutexLock lock(&mu_);
if (!ok) {
MaybeFinishLocked(Status(StatusCode::CANCELLED, "OnWriteDone() ok=false"));
return;
}
write_pending_ = false;
// If we got a new status since we started the last send, start a
// new send for it.
if (pending_status_ != NOT_FOUND) {
auto status = pending_status_;
pending_status_ = NOT_FOUND;
SendHealthLocked(status);
}
}
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchReactor::
OnCancel() {
grpc::internal::MutexLock lock(&mu_);
MaybeFinishLocked(Status(StatusCode::UNKNOWN, "OnCancel()"));
}
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchReactor::OnDone() {
gpr_log(GPR_DEBUG, "[HCS %p] watcher %p \"%s\": OnDone()", service_, this,
service_name_.c_str());
service_->database_->UnregisterWatch(service_name_, this);
{
grpc::internal::MutexLock lock(&service_->mu_);
if (--service_->num_watches_ == 0 && service_->shutdown_) {
service_->shutdown_condition_.Signal();
}
}
// Free the initial ref from instantiation.
Unref();
}
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchReactor::
MaybeFinishLocked(Status status) {
gpr_log(GPR_DEBUG,
"[HCS %p] watcher %p \"%s\": MaybeFinishLocked() with code=%d msg=%s",
service_, this, service_name_.c_str(), status.error_code(),
status.error_message().c_str());
if (!finish_called_) {
gpr_log(GPR_DEBUG, "[HCS %p] watcher %p \"%s\": actually calling Finish()",
service_, this, service_name_.c_str());
finish_called_ = true;
Finish(status);
}
}
} // namespace grpc

View File

@@ -0,0 +1,148 @@
//
//
// Copyright 2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#ifndef GRPC_SRC_CPP_SERVER_HEALTH_DEFAULT_HEALTH_CHECK_SERVICE_H
#define GRPC_SRC_CPP_SERVER_HEALTH_DEFAULT_HEALTH_CHECK_SERVICE_H
#include <stddef.h>
#include <map>
#include <memory>
#include <string>
#include "absl/base/thread_annotations.h"
#include <grpcpp/grpcpp.h>
#include <grpcpp/health_check_service_interface.h>
#include <grpcpp/impl/service_type.h>
#include <grpcpp/impl/sync.h>
#include <grpcpp/support/byte_buffer.h>
#include <grpcpp/support/server_callback.h>
#include <grpcpp/support/status.h>
#include "src/core/lib/gprpp/ref_counted.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
namespace grpc {
// Default implementation of HealthCheckServiceInterface. Server will create and
// own it.
class DefaultHealthCheckService final : public HealthCheckServiceInterface {
public:
enum ServingStatus { NOT_FOUND, SERVING, NOT_SERVING };
// The service impl to register with the server.
class HealthCheckServiceImpl : public Service {
public:
// Reactor for handling Watch streams.
class WatchReactor : public ServerWriteReactor<ByteBuffer>,
public grpc_core::RefCounted<WatchReactor> {
public:
WatchReactor(HealthCheckServiceImpl* service, const ByteBuffer* request);
void SendHealth(ServingStatus status);
void OnWriteDone(bool ok) override;
void OnCancel() override;
void OnDone() override;
private:
void SendHealthLocked(ServingStatus status)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&mu_);
void MaybeFinishLocked(Status status) ABSL_EXCLUSIVE_LOCKS_REQUIRED(&mu_);
HealthCheckServiceImpl* service_;
std::string service_name_;
ByteBuffer response_;
grpc::internal::Mutex mu_;
bool write_pending_ ABSL_GUARDED_BY(mu_) = false;
ServingStatus pending_status_ ABSL_GUARDED_BY(mu_) = NOT_FOUND;
bool finish_called_ ABSL_GUARDED_BY(mu_) = false;
};
explicit HealthCheckServiceImpl(DefaultHealthCheckService* database);
~HealthCheckServiceImpl() override;
private:
// Request handler for Check method.
static ServerUnaryReactor* HandleCheckRequest(
DefaultHealthCheckService* database, CallbackServerContext* context,
const ByteBuffer* request, ByteBuffer* response);
// Returns true on success.
static bool DecodeRequest(const ByteBuffer& request,
std::string* service_name);
static bool EncodeResponse(ServingStatus status, ByteBuffer* response);
DefaultHealthCheckService* database_;
grpc::internal::Mutex mu_;
grpc::internal::CondVar shutdown_condition_;
bool shutdown_ ABSL_GUARDED_BY(mu_) = false;
size_t num_watches_ ABSL_GUARDED_BY(mu_) = 0;
};
DefaultHealthCheckService();
void SetServingStatus(const std::string& service_name, bool serving) override;
void SetServingStatus(bool serving) override;
void Shutdown() override;
ServingStatus GetServingStatus(const std::string& service_name) const;
HealthCheckServiceImpl* GetHealthCheckService();
private:
// Stores the current serving status of a service and any call
// handlers registered for updates when the service's status changes.
class ServiceData {
public:
void SetServingStatus(ServingStatus status);
ServingStatus GetServingStatus() const { return status_; }
void AddWatch(
grpc_core::RefCountedPtr<HealthCheckServiceImpl::WatchReactor> watcher);
void RemoveWatch(HealthCheckServiceImpl::WatchReactor* watcher);
bool Unused() const { return watchers_.empty() && status_ == NOT_FOUND; }
private:
ServingStatus status_ = NOT_FOUND;
std::map<HealthCheckServiceImpl::WatchReactor*,
grpc_core::RefCountedPtr<HealthCheckServiceImpl::WatchReactor>>
watchers_;
};
void RegisterWatch(
const std::string& service_name,
grpc_core::RefCountedPtr<HealthCheckServiceImpl::WatchReactor> watcher);
void UnregisterWatch(const std::string& service_name,
HealthCheckServiceImpl::WatchReactor* watcher);
mutable grpc::internal::Mutex mu_;
bool shutdown_ ABSL_GUARDED_BY(&mu_) = false;
std::map<std::string, ServiceData> services_map_ ABSL_GUARDED_BY(&mu_);
std::unique_ptr<HealthCheckServiceImpl> impl_;
};
} // namespace grpc
#endif // GRPC_SRC_CPP_SERVER_HEALTH_DEFAULT_HEALTH_CHECK_SERVICE_H

View File

@@ -0,0 +1,34 @@
//
//
// Copyright 2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <grpcpp/health_check_service_interface.h>
namespace grpc {
namespace {
bool g_grpc_default_health_check_service_enabled = false;
} // namespace
bool DefaultHealthCheckServiceEnabled() {
return g_grpc_default_health_check_service_enabled;
}
void EnableDefaultHealthCheckService(bool enable) {
g_grpc_default_health_check_service_enabled = enable;
}
} // namespace grpc

View File

@@ -0,0 +1,42 @@
//
//
// Copyright 2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <memory>
#include <utility>
#include <vector>
#include <grpcpp/ext/health_check_service_server_builder_option.h>
#include <grpcpp/health_check_service_interface.h>
#include <grpcpp/impl/server_builder_plugin.h>
#include <grpcpp/support/channel_arguments.h>
namespace grpc {
HealthCheckServiceServerBuilderOption::HealthCheckServiceServerBuilderOption(
std::unique_ptr<HealthCheckServiceInterface> hc)
: hc_(std::move(hc)) {}
// Hand over hc_ to the server.
void HealthCheckServiceServerBuilderOption::UpdateArguments(
ChannelArguments* args) {
args->SetPointer(kHealthCheckServiceInterfaceArg, hc_.release());
}
void HealthCheckServiceServerBuilderOption::UpdatePlugins(
std::vector<std::unique_ptr<ServerBuilderPlugin>>* /*plugins*/) {}
} // namespace grpc

View File

@@ -0,0 +1,55 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <memory>
#include <string>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpc/support/log.h>
#include <grpcpp/security/auth_metadata_processor.h>
#include <grpcpp/security/server_credentials.h>
namespace grpc {
namespace {
class InsecureServerCredentialsImpl final : public ServerCredentials {
public:
int AddPortToServer(const std::string& addr, grpc_server* server) override {
grpc_server_credentials* server_creds =
grpc_insecure_server_credentials_create();
int result = grpc_server_add_http2_port(server, addr.c_str(), server_creds);
grpc_server_credentials_release(server_creds);
return result;
}
void SetAuthMetadataProcessor(
const std::shared_ptr<grpc::AuthMetadataProcessor>& processor) override {
(void)processor;
GPR_ASSERT(0); // Should not be called on InsecureServerCredentials.
}
private:
bool IsInsecure() const override { return true; }
};
} // namespace
std::shared_ptr<ServerCredentials> InsecureServerCredentials() {
return std::shared_ptr<ServerCredentials>(
new InsecureServerCredentialsImpl());
}
} // namespace grpc

View File

@@ -0,0 +1,164 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include "src/cpp/server/secure_server_credentials.h"
#include <algorithm>
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include <grpc/grpc_security_constants.h>
#include <grpc/status.h>
#include <grpcpp/security/auth_metadata_processor.h>
#include <grpcpp/security/tls_credentials_options.h>
#include <grpcpp/support/slice.h>
#include <grpcpp/support/status.h>
#include <grpcpp/support/string_ref.h>
#include "src/cpp/common/secure_auth_context.h"
namespace grpc {
void AuthMetadataProcessorAsyncWrapper::Destroy(void* wrapper) {
auto* w = static_cast<AuthMetadataProcessorAsyncWrapper*>(wrapper);
delete w;
}
void AuthMetadataProcessorAsyncWrapper::Process(
void* wrapper, grpc_auth_context* context, const grpc_metadata* md,
size_t num_md, grpc_process_auth_metadata_done_cb cb, void* user_data) {
auto* w = static_cast<AuthMetadataProcessorAsyncWrapper*>(wrapper);
if (!w->processor_) {
// Early exit.
cb(user_data, nullptr, 0, nullptr, 0, GRPC_STATUS_OK, nullptr);
return;
}
if (w->processor_->IsBlocking()) {
w->thread_pool_->Add([w, context, md, num_md, cb, user_data] {
w->AuthMetadataProcessorAsyncWrapper::InvokeProcessor(context, md, num_md,
cb, user_data);
});
} else {
// invoke directly.
w->InvokeProcessor(context, md, num_md, cb, user_data);
}
}
void AuthMetadataProcessorAsyncWrapper::InvokeProcessor(
grpc_auth_context* context, const grpc_metadata* md, size_t num_md,
grpc_process_auth_metadata_done_cb cb, void* user_data) {
AuthMetadataProcessor::InputMetadata metadata;
for (size_t i = 0; i < num_md; i++) {
metadata.insert(std::make_pair(StringRefFromSlice(&md[i].key),
StringRefFromSlice(&md[i].value)));
}
SecureAuthContext ctx(context);
AuthMetadataProcessor::OutputMetadata consumed_metadata;
AuthMetadataProcessor::OutputMetadata response_metadata;
Status status = processor_->Process(metadata, &ctx, &consumed_metadata,
&response_metadata);
std::vector<grpc_metadata> consumed_md;
for (const auto& consumed : consumed_metadata) {
grpc_metadata md_entry;
md_entry.key = SliceReferencingString(consumed.first);
md_entry.value = SliceReferencingString(consumed.second);
consumed_md.push_back(md_entry);
}
std::vector<grpc_metadata> response_md;
for (const auto& response : response_metadata) {
grpc_metadata md_entry;
md_entry.key = SliceReferencingString(response.first);
md_entry.value = SliceReferencingString(response.second);
response_md.push_back(md_entry);
}
auto consumed_md_data = consumed_md.empty() ? nullptr : &consumed_md[0];
auto response_md_data = response_md.empty() ? nullptr : &response_md[0];
cb(user_data, consumed_md_data, consumed_md.size(), response_md_data,
response_md.size(), static_cast<grpc_status_code>(status.error_code()),
status.error_message().c_str());
}
int SecureServerCredentials::AddPortToServer(const std::string& addr,
grpc_server* server) {
return grpc_server_add_http2_port(server, addr.c_str(), creds_);
}
void SecureServerCredentials::SetAuthMetadataProcessor(
const std::shared_ptr<grpc::AuthMetadataProcessor>& processor) {
auto* wrapper = new grpc::AuthMetadataProcessorAsyncWrapper(processor);
grpc_server_credentials_set_auth_metadata_processor(
creds_, {grpc::AuthMetadataProcessorAsyncWrapper::Process,
grpc::AuthMetadataProcessorAsyncWrapper::Destroy, wrapper});
}
std::shared_ptr<ServerCredentials> SslServerCredentials(
const grpc::SslServerCredentialsOptions& options) {
std::vector<grpc_ssl_pem_key_cert_pair> pem_key_cert_pairs;
for (const auto& key_cert_pair : options.pem_key_cert_pairs) {
grpc_ssl_pem_key_cert_pair p = {key_cert_pair.private_key.c_str(),
key_cert_pair.cert_chain.c_str()};
pem_key_cert_pairs.push_back(p);
}
grpc_server_credentials* c_creds = grpc_ssl_server_credentials_create_ex(
options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(),
pem_key_cert_pairs.empty() ? nullptr : &pem_key_cert_pairs[0],
pem_key_cert_pairs.size(),
options.force_client_auth
? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
: options.client_certificate_request,
nullptr);
return std::shared_ptr<ServerCredentials>(
new SecureServerCredentials(c_creds));
}
namespace experimental {
std::shared_ptr<ServerCredentials> AltsServerCredentials(
const AltsServerCredentialsOptions& /* options */) {
grpc_alts_credentials_options* c_options =
grpc_alts_credentials_server_options_create();
grpc_server_credentials* c_creds =
grpc_alts_server_credentials_create(c_options);
grpc_alts_credentials_options_destroy(c_options);
return std::shared_ptr<ServerCredentials>(
new SecureServerCredentials(c_creds));
}
std::shared_ptr<ServerCredentials> LocalServerCredentials(
grpc_local_connect_type type) {
return std::shared_ptr<ServerCredentials>(
new SecureServerCredentials(grpc_local_server_credentials_create(type)));
}
std::shared_ptr<ServerCredentials> TlsServerCredentials(
const grpc::experimental::TlsServerCredentialsOptions& options) {
grpc_server_credentials* c_creds =
grpc_tls_server_credentials_create(options.c_credentials_options());
if (c_creds == nullptr) {
return nullptr;
}
return std::shared_ptr<ServerCredentials>(
new SecureServerCredentials(c_creds));
}
} // namespace experimental
} // namespace grpc

View File

@@ -0,0 +1,86 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#ifndef GRPC_SRC_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H
#define GRPC_SRC_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H
#include <stddef.h>
#include <memory>
#include <string>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpcpp/security/auth_metadata_processor.h>
#include <grpcpp/security/server_credentials.h>
#include "src/cpp/server/thread_pool_interface.h"
namespace grpc {
class SecureServerCredentials;
class AuthMetadataProcessorAsyncWrapper final {
public:
static void Destroy(void* wrapper);
static void Process(void* wrapper, grpc_auth_context* context,
const grpc_metadata* md, size_t num_md,
grpc_process_auth_metadata_done_cb cb, void* user_data);
explicit AuthMetadataProcessorAsyncWrapper(
const std::shared_ptr<AuthMetadataProcessor>& processor)
: processor_(processor) {
if (processor && processor->IsBlocking()) {
thread_pool_.reset(CreateDefaultThreadPool());
}
}
private:
void InvokeProcessor(grpc_auth_context* context, const grpc_metadata* md,
size_t num_md, grpc_process_auth_metadata_done_cb cb,
void* user_data);
std::unique_ptr<ThreadPoolInterface> thread_pool_;
std::shared_ptr<AuthMetadataProcessor> processor_;
};
class SecureServerCredentials final : public ServerCredentials {
public:
explicit SecureServerCredentials(grpc_server_credentials* creds)
: creds_(creds) {}
~SecureServerCredentials() override {
grpc_server_credentials_release(creds_);
}
int AddPortToServer(const std::string& addr, grpc_server* server) override;
void SetAuthMetadataProcessor(
const std::shared_ptr<grpc::AuthMetadataProcessor>& processor) override;
grpc_server_credentials* c_creds() { return creds_; }
private:
SecureServerCredentials* AsSecureServerCredentials() override { return this; }
grpc_server_credentials* creds_;
std::unique_ptr<grpc::AuthMetadataProcessorAsyncWrapper> processor_;
};
} // namespace grpc
#endif // GRPC_SRC_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H

View File

@@ -0,0 +1,471 @@
//
//
// Copyright 2015-2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <limits.h>
#include <string.h>
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <grpc/grpc.h>
#include <grpc/impl/channel_arg_names.h>
#include <grpc/impl/compression_types.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
#include <grpc/support/workaround_list.h>
#include <grpcpp/completion_queue.h>
#include <grpcpp/impl/server_builder_option.h>
#include <grpcpp/impl/server_builder_plugin.h>
#include <grpcpp/impl/service_type.h>
#include <grpcpp/resource_quota.h>
#include <grpcpp/security/authorization_policy_provider.h>
#include <grpcpp/security/server_credentials.h>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
#include <grpcpp/server_context.h>
#include <grpcpp/server_interface.h>
#include <grpcpp/support/channel_arguments.h>
#include <grpcpp/support/server_interceptor.h>
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/gpr/useful.h"
#include "src/cpp/server/external_connection_acceptor_impl.h"
namespace grpc {
static std::vector<std::unique_ptr<ServerBuilderPlugin> (*)()>*
g_plugin_factory_list;
static gpr_once once_init_plugin_list = GPR_ONCE_INIT;
static void do_plugin_list_init(void) {
g_plugin_factory_list =
new std::vector<std::unique_ptr<ServerBuilderPlugin> (*)()>();
}
ServerBuilder::ServerBuilder()
: max_receive_message_size_(INT_MIN),
max_send_message_size_(INT_MIN),
sync_server_settings_(SyncServerSettings()),
resource_quota_(nullptr) {
gpr_once_init(&once_init_plugin_list, do_plugin_list_init);
for (const auto& value : *g_plugin_factory_list) {
plugins_.emplace_back(value());
}
// all compression algorithms enabled by default.
enabled_compression_algorithms_bitset_ =
(1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1;
memset(&maybe_default_compression_level_, 0,
sizeof(maybe_default_compression_level_));
memset(&maybe_default_compression_algorithm_, 0,
sizeof(maybe_default_compression_algorithm_));
}
ServerBuilder::~ServerBuilder() {
if (resource_quota_ != nullptr) {
grpc_resource_quota_unref(resource_quota_);
}
}
std::unique_ptr<grpc::ServerCompletionQueue> ServerBuilder::AddCompletionQueue(
bool is_frequently_polled) {
grpc::ServerCompletionQueue* cq = new grpc::ServerCompletionQueue(
GRPC_CQ_NEXT,
is_frequently_polled ? GRPC_CQ_DEFAULT_POLLING : GRPC_CQ_NON_LISTENING,
nullptr);
cqs_.push_back(cq);
return std::unique_ptr<grpc::ServerCompletionQueue>(cq);
}
ServerBuilder& ServerBuilder::RegisterService(Service* service) {
services_.emplace_back(new NamedService(service));
return *this;
}
ServerBuilder& ServerBuilder::RegisterService(const std::string& host,
Service* service) {
services_.emplace_back(new NamedService(host, service));
return *this;
}
ServerBuilder& ServerBuilder::RegisterAsyncGenericService(
AsyncGenericService* service) {
if (generic_service_ || callback_generic_service_) {
gpr_log(GPR_ERROR,
"Adding multiple generic services is unsupported for now. "
"Dropping the service %p",
service);
} else {
generic_service_ = service;
}
return *this;
}
ServerBuilder& ServerBuilder::RegisterCallbackGenericService(
CallbackGenericService* service) {
if (generic_service_ || callback_generic_service_) {
gpr_log(GPR_ERROR,
"Adding multiple generic services is unsupported for now. "
"Dropping the service %p",
service);
} else {
callback_generic_service_ = service;
}
return *this;
}
ServerBuilder& ServerBuilder::SetContextAllocator(
std::unique_ptr<grpc::ContextAllocator> context_allocator) {
context_allocator_ = std::move(context_allocator);
return *this;
}
std::unique_ptr<grpc::experimental::ExternalConnectionAcceptor>
ServerBuilder::experimental_type::AddExternalConnectionAcceptor(
experimental_type::ExternalConnectionType type,
std::shared_ptr<ServerCredentials> creds) {
std::string name_prefix("external:");
char count_str[GPR_LTOA_MIN_BUFSIZE];
gpr_ltoa(static_cast<long>(builder_->acceptors_.size()), count_str);
builder_->acceptors_.emplace_back(
std::make_shared<grpc::internal::ExternalConnectionAcceptorImpl>(
name_prefix.append(count_str), type, creds));
return builder_->acceptors_.back()->GetAcceptor();
}
void ServerBuilder::experimental_type::SetAuthorizationPolicyProvider(
std::shared_ptr<experimental::AuthorizationPolicyProviderInterface>
provider) {
builder_->authorization_provider_ = std::move(provider);
}
void ServerBuilder::experimental_type::EnableCallMetricRecording(
experimental::ServerMetricRecorder* server_metric_recorder) {
builder_->AddChannelArgument(GRPC_ARG_SERVER_CALL_METRIC_RECORDING, 1);
GPR_ASSERT(builder_->server_metric_recorder_ == nullptr);
builder_->server_metric_recorder_ = server_metric_recorder;
}
ServerBuilder& ServerBuilder::SetOption(
std::unique_ptr<ServerBuilderOption> option) {
options_.push_back(std::move(option));
return *this;
}
ServerBuilder& ServerBuilder::SetSyncServerOption(
ServerBuilder::SyncServerOption option, int val) {
switch (option) {
case NUM_CQS:
sync_server_settings_.num_cqs = val;
break;
case MIN_POLLERS:
sync_server_settings_.min_pollers = val;
break;
case MAX_POLLERS:
sync_server_settings_.max_pollers = val;
break;
case CQ_TIMEOUT_MSEC:
sync_server_settings_.cq_timeout_msec = val;
break;
}
return *this;
}
ServerBuilder& ServerBuilder::SetCompressionAlgorithmSupportStatus(
grpc_compression_algorithm algorithm, bool enabled) {
if (enabled) {
grpc_core::SetBit(&enabled_compression_algorithms_bitset_, algorithm);
} else {
grpc_core::ClearBit(&enabled_compression_algorithms_bitset_, algorithm);
}
return *this;
}
ServerBuilder& ServerBuilder::SetDefaultCompressionLevel(
grpc_compression_level level) {
maybe_default_compression_level_.is_set = true;
maybe_default_compression_level_.level = level;
return *this;
}
ServerBuilder& ServerBuilder::SetDefaultCompressionAlgorithm(
grpc_compression_algorithm algorithm) {
maybe_default_compression_algorithm_.is_set = true;
maybe_default_compression_algorithm_.algorithm = algorithm;
return *this;
}
ServerBuilder& ServerBuilder::SetResourceQuota(
const grpc::ResourceQuota& resource_quota) {
if (resource_quota_ != nullptr) {
grpc_resource_quota_unref(resource_quota_);
}
resource_quota_ = resource_quota.c_resource_quota();
grpc_resource_quota_ref(resource_quota_);
return *this;
}
ServerBuilder& ServerBuilder::AddListeningPort(
const std::string& addr_uri, std::shared_ptr<ServerCredentials> creds,
int* selected_port) {
const std::string uri_scheme = "dns:";
std::string addr = addr_uri;
if (addr_uri.compare(0, uri_scheme.size(), uri_scheme) == 0) {
size_t pos = uri_scheme.size();
while (addr_uri[pos] == '/') ++pos; // Skip slashes.
addr = addr_uri.substr(pos);
}
Port port = {addr, std::move(creds), selected_port};
ports_.push_back(port);
return *this;
}
ChannelArguments ServerBuilder::BuildChannelArgs() {
ChannelArguments args;
if (max_receive_message_size_ >= -1) {
args.SetInt(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, max_receive_message_size_);
}
if (max_send_message_size_ >= -1) {
args.SetInt(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH, max_send_message_size_);
}
for (const auto& option : options_) {
option->UpdateArguments(&args);
option->UpdatePlugins(&plugins_);
}
args.SetInt(GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET,
enabled_compression_algorithms_bitset_);
if (maybe_default_compression_level_.is_set) {
args.SetInt(GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL,
maybe_default_compression_level_.level);
}
if (maybe_default_compression_algorithm_.is_set) {
args.SetInt(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM,
maybe_default_compression_algorithm_.algorithm);
}
if (resource_quota_ != nullptr) {
args.SetPointerWithVtable(GRPC_ARG_RESOURCE_QUOTA, resource_quota_,
grpc_resource_quota_arg_vtable());
}
for (const auto& plugin : plugins_) {
plugin->UpdateServerBuilder(this);
plugin->UpdateChannelArguments(&args);
}
if (authorization_provider_ != nullptr) {
args.SetPointerWithVtable(GRPC_ARG_AUTHORIZATION_POLICY_PROVIDER,
authorization_provider_->c_provider(),
grpc_authorization_policy_provider_arg_vtable());
}
return args;
}
std::unique_ptr<grpc::Server> ServerBuilder::BuildAndStart() {
ChannelArguments args = BuildChannelArgs();
// == Determine if the server has any syncrhonous methods ==
bool has_sync_methods = false;
for (const auto& value : services_) {
if (value->service->has_synchronous_methods()) {
has_sync_methods = true;
break;
}
}
if (!has_sync_methods) {
for (const auto& value : plugins_) {
if (value->has_sync_methods()) {
has_sync_methods = true;
break;
}
}
}
// If this is a Sync server, i.e a server expositing sync API, then the server
// needs to create some completion queues to listen for incoming requests.
// 'sync_server_cqs' are those internal completion queues.
//
// This is different from the completion queues added to the server via
// ServerBuilder's AddCompletionQueue() method (those completion queues
// are in 'cqs_' member variable of ServerBuilder object)
std::shared_ptr<std::vector<std::unique_ptr<grpc::ServerCompletionQueue>>>
sync_server_cqs(
std::make_shared<
std::vector<std::unique_ptr<grpc::ServerCompletionQueue>>>());
bool has_frequently_polled_cqs = false;
for (const auto& cq : cqs_) {
if (cq->IsFrequentlyPolled()) {
has_frequently_polled_cqs = true;
break;
}
}
// == Determine if the server has any callback methods ==
bool has_callback_methods = false;
for (const auto& service : services_) {
if (service->service->has_callback_methods()) {
has_callback_methods = true;
has_frequently_polled_cqs = true;
break;
}
}
if (callback_generic_service_ != nullptr) {
has_frequently_polled_cqs = true;
}
const bool is_hybrid_server = has_sync_methods && has_frequently_polled_cqs;
if (has_sync_methods) {
grpc_cq_polling_type polling_type =
is_hybrid_server ? GRPC_CQ_NON_POLLING : GRPC_CQ_DEFAULT_POLLING;
// Create completion queues to listen to incoming rpc requests
for (int i = 0; i < sync_server_settings_.num_cqs; i++) {
sync_server_cqs->emplace_back(
new grpc::ServerCompletionQueue(GRPC_CQ_NEXT, polling_type, nullptr));
}
}
// TODO(vjpai): Add a section here for plugins once they can support callback
// methods
if (has_sync_methods) {
// This is a Sync server
gpr_log(GPR_INFO,
"Synchronous server. Num CQs: %d, Min pollers: %d, Max Pollers: "
"%d, CQ timeout (msec): %d",
sync_server_settings_.num_cqs, sync_server_settings_.min_pollers,
sync_server_settings_.max_pollers,
sync_server_settings_.cq_timeout_msec);
}
if (has_callback_methods) {
gpr_log(GPR_INFO, "Callback server.");
}
std::unique_ptr<grpc::Server> server(new grpc::Server(
&args, sync_server_cqs, sync_server_settings_.min_pollers,
sync_server_settings_.max_pollers, sync_server_settings_.cq_timeout_msec,
std::move(acceptors_), server_config_fetcher_, resource_quota_,
std::move(interceptor_creators_), server_metric_recorder_));
ServerInitializer* initializer = server->initializer();
// Register all the completion queues with the server. i.e
// 1. sync_server_cqs: internal completion queues created IF this is a sync
// server
// 2. cqs_: Completion queues added via AddCompletionQueue() call
for (const auto& cq : *sync_server_cqs) {
grpc_server_register_completion_queue(server->server_, cq->cq(), nullptr);
has_frequently_polled_cqs = true;
}
if (has_callback_methods || callback_generic_service_ != nullptr) {
auto* cq = server->CallbackCQ();
grpc_server_register_completion_queue(server->server_, cq->cq(), nullptr);
}
// cqs_ contains the completion queue added by calling the ServerBuilder's
// AddCompletionQueue() API. Some of them may not be frequently polled (i.e by
// calling Next() or AsyncNext()) and hence are not safe to be used for
// listening to incoming channels. Such completion queues must be registered
// as non-listening queues. In debug mode, these should have their server list
// tracked since these are provided the user and must be Shutdown by the user
// after the server is shutdown.
for (const auto& cq : cqs_) {
grpc_server_register_completion_queue(server->server_, cq->cq(), nullptr);
cq->RegisterServer(server.get());
}
if (!has_frequently_polled_cqs) {
gpr_log(GPR_ERROR,
"At least one of the completion queues must be frequently polled");
return nullptr;
}
server->RegisterContextAllocator(std::move(context_allocator_));
for (const auto& value : services_) {
if (!server->RegisterService(value->host.get(), value->service)) {
return nullptr;
}
}
for (const auto& value : plugins_) {
value->InitServer(initializer);
}
if (generic_service_) {
server->RegisterAsyncGenericService(generic_service_);
} else if (callback_generic_service_) {
server->RegisterCallbackGenericService(callback_generic_service_);
} else {
for (const auto& value : services_) {
if (value->service->has_generic_methods()) {
gpr_log(GPR_ERROR,
"Some methods were marked generic but there is no "
"generic service registered.");
return nullptr;
}
}
}
bool added_port = false;
for (auto& port : ports_) {
int r = server->AddListeningPort(port.addr, port.creds.get());
if (!r) {
if (added_port) server->Shutdown();
return nullptr;
}
added_port = true;
if (port.selected_port != nullptr) {
*port.selected_port = r;
}
}
auto cqs_data = cqs_.empty() ? nullptr : &cqs_[0];
server->Start(cqs_data, cqs_.size());
for (const auto& value : plugins_) {
value->Finish(initializer);
}
return server;
}
void ServerBuilder::InternalAddPluginFactory(
std::unique_ptr<ServerBuilderPlugin> (*CreatePlugin)()) {
gpr_once_init(&once_init_plugin_list, do_plugin_list_init);
(*g_plugin_factory_list).push_back(CreatePlugin);
}
ServerBuilder& ServerBuilder::EnableWorkaround(grpc_workaround_list id) {
switch (id) {
case GRPC_WORKAROUND_ID_CRONET_COMPRESSION:
return AddChannelArgument(GRPC_ARG_WORKAROUND_CRONET_COMPRESSION, 1);
default:
gpr_log(GPR_ERROR, "Workaround %u does not exist or is obsolete.", id);
return *this;
}
}
} // namespace grpc

View File

@@ -0,0 +1,87 @@
//
// Copyright 2019 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include "absl/status/status.h"
#include <grpcpp/support/server_callback.h>
#include "src/core/lib/iomgr/closure.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/iomgr/executor.h"
namespace grpc {
namespace internal {
void ServerCallbackCall::ScheduleOnDone(bool inline_ondone) {
if (inline_ondone) {
CallOnDone();
} else {
// Unlike other uses of closure, do not Ref or Unref here since at this
// point, all the Ref'fing and Unref'fing is done for this call.
grpc_core::ExecCtx exec_ctx;
struct ClosureWithArg {
grpc_closure closure;
ServerCallbackCall* call;
explicit ClosureWithArg(ServerCallbackCall* call_arg) : call(call_arg) {
GRPC_CLOSURE_INIT(
&closure,
[](void* void_arg, grpc_error_handle) {
ClosureWithArg* arg = static_cast<ClosureWithArg*>(void_arg);
arg->call->CallOnDone();
delete arg;
},
this, grpc_schedule_on_exec_ctx);
}
};
ClosureWithArg* arg = new ClosureWithArg(this);
grpc_core::Executor::Run(&arg->closure, absl::OkStatus());
}
}
void ServerCallbackCall::CallOnCancel(ServerReactor* reactor) {
if (reactor->InternalInlineable()) {
reactor->OnCancel();
} else {
// Ref to make sure that the closure executes before the whole call gets
// destructed, and Unref within the closure.
Ref();
grpc_core::ExecCtx exec_ctx;
struct ClosureWithArg {
grpc_closure closure;
ServerCallbackCall* call;
ServerReactor* reactor;
ClosureWithArg(ServerCallbackCall* call_arg, ServerReactor* reactor_arg)
: call(call_arg), reactor(reactor_arg) {
GRPC_CLOSURE_INIT(
&closure,
[](void* void_arg, grpc_error_handle) {
ClosureWithArg* arg = static_cast<ClosureWithArg*>(void_arg);
arg->reactor->OnCancel();
arg->call->MaybeDone();
delete arg;
},
this, grpc_schedule_on_exec_ctx);
}
};
ClosureWithArg* arg = new ClosureWithArg(this, reactor);
grpc_core::Executor::Run(&arg->closure, absl::OkStatus());
}
}
} // namespace internal
} // namespace grpc

1397
Pods/gRPC-C++/src/cpp/server/server_cc.cc generated Normal file
View File

@@ -0,0 +1,1397 @@
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <limits.h>
#include <string.h>
#include <algorithm>
#include <atomic>
#include <cstdlib>
#include <memory>
#include <new>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "absl/status/status.h"
#include <grpc/byte_buffer.h>
#include <grpc/grpc.h>
#include <grpc/impl/channel_arg_names.h>
#include <grpc/slice.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
#include <grpc/support/time.h>
#include <grpcpp/channel.h>
#include <grpcpp/completion_queue.h>
#include <grpcpp/generic/async_generic_service.h>
#include <grpcpp/health_check_service_interface.h>
#include <grpcpp/impl/call.h>
#include <grpcpp/impl/call_op_set.h>
#include <grpcpp/impl/call_op_set_interface.h>
#include <grpcpp/impl/completion_queue_tag.h>
#include <grpcpp/impl/interceptor_common.h>
#include <grpcpp/impl/metadata_map.h>
#include <grpcpp/impl/rpc_method.h>
#include <grpcpp/impl/rpc_service_method.h>
#include <grpcpp/impl/server_callback_handlers.h>
#include <grpcpp/impl/server_initializer.h>
#include <grpcpp/impl/service_type.h>
#include <grpcpp/impl/sync.h>
#include <grpcpp/security/server_credentials.h>
#include <grpcpp/server.h>
#include <grpcpp/server_context.h>
#include <grpcpp/server_interface.h>
#include <grpcpp/support/byte_buffer.h>
#include <grpcpp/support/channel_arguments.h>
#include <grpcpp/support/client_interceptor.h>
#include <grpcpp/support/interceptor.h>
#include <grpcpp/support/method_handler.h>
#include <grpcpp/support/server_interceptor.h>
#include <grpcpp/support/slice.h>
#include <grpcpp/support/status.h>
#include "src/core/ext/transport/inproc/inproc_transport.h"
#include "src/core/lib/gprpp/manual_constructor.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/iomgr/iomgr.h"
#include "src/core/lib/resource_quota/api.h"
#include "src/core/lib/surface/completion_queue.h"
#include "src/core/lib/surface/server.h"
#include "src/cpp/client/create_channel_internal.h"
#include "src/cpp/server/external_connection_acceptor_impl.h"
#include "src/cpp/server/health/default_health_check_service.h"
#include "src/cpp/thread_manager/thread_manager.h"
namespace grpc {
namespace {
// The default value for maximum number of threads that can be created in the
// sync server. This value of INT_MAX is chosen to match the default behavior if
// no ResourceQuota is set. To modify the max number of threads in a sync
// server, pass a custom ResourceQuota object (with the desired number of
// max-threads set) to the server builder.
#define DEFAULT_MAX_SYNC_SERVER_THREADS INT_MAX
// Give a useful status error message if the resource is exhausted specifically
// because the server threadpool is full.
const char* kServerThreadpoolExhausted = "Server Threadpool Exhausted";
// Although we might like to give a useful status error message on unimplemented
// RPCs, it's not always possible since that also would need to be added across
// languages and isn't actually required by the spec.
const char* kUnknownRpcMethod = "";
class DefaultGlobalCallbacks final : public Server::GlobalCallbacks {
public:
~DefaultGlobalCallbacks() override {}
void PreSynchronousRequest(ServerContext* /*context*/) override {}
void PostSynchronousRequest(ServerContext* /*context*/) override {}
};
std::shared_ptr<Server::GlobalCallbacks> g_callbacks = nullptr;
gpr_once g_once_init_callbacks = GPR_ONCE_INIT;
void InitGlobalCallbacks() {
if (!g_callbacks) {
g_callbacks.reset(new DefaultGlobalCallbacks());
}
}
class ShutdownTag : public internal::CompletionQueueTag {
public:
bool FinalizeResult(void** /*tag*/, bool* /*status*/) override {
return false;
}
};
class PhonyTag : public internal::CompletionQueueTag {
public:
bool FinalizeResult(void** /*tag*/, bool* /*status*/) override {
return true;
}
};
class UnimplementedAsyncRequestContext {
protected:
UnimplementedAsyncRequestContext() : generic_stream_(&server_context_) {}
GenericServerContext server_context_;
GenericServerAsyncReaderWriter generic_stream_;
};
} // namespace
ServerInterface::BaseAsyncRequest::BaseAsyncRequest(
ServerInterface* server, ServerContext* context,
internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
ServerCompletionQueue* notification_cq, void* tag, bool delete_on_finalize)
: server_(server),
context_(context),
stream_(stream),
call_cq_(call_cq),
notification_cq_(notification_cq),
tag_(tag),
delete_on_finalize_(delete_on_finalize),
call_(nullptr),
done_intercepting_(false) {
// Set up interception state partially for the receive ops. call_wrapper_ is
// not filled at this point, but it will be filled before the interceptors are
// run.
interceptor_methods_.SetCall(&call_wrapper_);
interceptor_methods_.SetReverse();
call_cq_->RegisterAvalanching(); // This op will trigger more ops
call_metric_recording_enabled_ = server_->call_metric_recording_enabled();
server_metric_recorder_ = server_->server_metric_recorder();
}
ServerInterface::BaseAsyncRequest::~BaseAsyncRequest() {
call_cq_->CompleteAvalanching();
}
bool ServerInterface::BaseAsyncRequest::FinalizeResult(void** tag,
bool* status) {
if (done_intercepting_) {
*tag = tag_;
if (delete_on_finalize_) {
delete this;
}
return true;
}
context_->set_call(call_, call_metric_recording_enabled_,
server_metric_recorder_);
context_->cq_ = call_cq_;
if (call_wrapper_.call() == nullptr) {
// Fill it since it is empty.
call_wrapper_ = internal::Call(
call_, server_, call_cq_, server_->max_receive_message_size(), nullptr);
}
// just the pointers inside call are copied here
stream_->BindCall(&call_wrapper_);
if (*status && call_ && call_wrapper_.server_rpc_info()) {
done_intercepting_ = true;
// Set interception point for RECV INITIAL METADATA
interceptor_methods_.AddInterceptionHookPoint(
experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA);
interceptor_methods_.SetRecvInitialMetadata(&context_->client_metadata_);
if (interceptor_methods_.RunInterceptors(
[this]() { ContinueFinalizeResultAfterInterception(); })) {
// There are no interceptors to run. Continue
} else {
// There were interceptors to be run, so
// ContinueFinalizeResultAfterInterception will be run when interceptors
// are done.
return false;
}
}
if (*status && call_) {
context_->BeginCompletionOp(&call_wrapper_, nullptr, nullptr);
}
*tag = tag_;
if (delete_on_finalize_) {
delete this;
}
return true;
}
void ServerInterface::BaseAsyncRequest::
ContinueFinalizeResultAfterInterception() {
context_->BeginCompletionOp(&call_wrapper_, nullptr, nullptr);
// Queue a tag which will be returned immediately
grpc_core::ExecCtx exec_ctx;
grpc_cq_begin_op(notification_cq_->cq(), this);
grpc_cq_end_op(
notification_cq_->cq(), this, absl::OkStatus(),
[](void* /*arg*/, grpc_cq_completion* completion) { delete completion; },
nullptr, new grpc_cq_completion());
}
ServerInterface::RegisteredAsyncRequest::RegisteredAsyncRequest(
ServerInterface* server, ServerContext* context,
internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
ServerCompletionQueue* notification_cq, void* tag, const char* name,
internal::RpcMethod::RpcType type)
: BaseAsyncRequest(server, context, stream, call_cq, notification_cq, tag,
true),
name_(name),
type_(type) {}
void ServerInterface::RegisteredAsyncRequest::IssueRequest(
void* registered_method, grpc_byte_buffer** payload,
ServerCompletionQueue* notification_cq) {
// The following call_start_batch is internally-generated so no need for an
// explanatory log on failure.
GPR_ASSERT(grpc_server_request_registered_call(
server_->server(), registered_method, &call_,
&context_->deadline_, context_->client_metadata_.arr(),
payload, call_cq_->cq(), notification_cq->cq(),
this) == GRPC_CALL_OK);
}
ServerInterface::GenericAsyncRequest::GenericAsyncRequest(
ServerInterface* server, GenericServerContext* context,
internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
ServerCompletionQueue* notification_cq, void* tag, bool delete_on_finalize,
bool issue_request)
: BaseAsyncRequest(server, context, stream, call_cq, notification_cq, tag,
delete_on_finalize) {
grpc_call_details_init(&call_details_);
GPR_ASSERT(notification_cq);
GPR_ASSERT(call_cq);
if (issue_request) {
IssueRequest();
}
}
bool ServerInterface::GenericAsyncRequest::FinalizeResult(void** tag,
bool* status) {
// If we are done intercepting, there is nothing more for us to do
if (done_intercepting_) {
return BaseAsyncRequest::FinalizeResult(tag, status);
}
// TODO(yangg) remove the copy here.
if (*status) {
static_cast<GenericServerContext*>(context_)->method_ =
StringFromCopiedSlice(call_details_.method);
static_cast<GenericServerContext*>(context_)->host_ =
StringFromCopiedSlice(call_details_.host);
context_->deadline_ = call_details_.deadline;
}
grpc_slice_unref(call_details_.method);
grpc_slice_unref(call_details_.host);
call_wrapper_ = internal::Call(
call_, server_, call_cq_, server_->max_receive_message_size(),
context_->set_server_rpc_info(
static_cast<GenericServerContext*>(context_)->method_.c_str(),
internal::RpcMethod::BIDI_STREAMING,
*server_->interceptor_creators()));
return BaseAsyncRequest::FinalizeResult(tag, status);
}
void ServerInterface::GenericAsyncRequest::IssueRequest() {
// The following call_start_batch is internally-generated so no need for an
// explanatory log on failure.
GPR_ASSERT(grpc_server_request_call(server_->server(), &call_, &call_details_,
context_->client_metadata_.arr(),
call_cq_->cq(), notification_cq_->cq(),
this) == GRPC_CALL_OK);
}
namespace {
class ShutdownCallback : public grpc_completion_queue_functor {
public:
ShutdownCallback() {
functor_run = &ShutdownCallback::Run;
// Set inlineable to true since this callback is trivial and thus does not
// need to be run from the executor (triggering a thread hop). This should
// only be used by internal callbacks like this and not by user application
// code.
inlineable = true;
}
// TakeCQ takes ownership of the cq into the shutdown callback
// so that the shutdown callback will be responsible for destroying it
void TakeCQ(CompletionQueue* cq) { cq_ = cq; }
// The Run function will get invoked by the completion queue library
// when the shutdown is actually complete
static void Run(grpc_completion_queue_functor* cb, int) {
auto* callback = static_cast<ShutdownCallback*>(cb);
delete callback->cq_;
delete callback;
}
private:
CompletionQueue* cq_ = nullptr;
};
} // namespace
/// Use private inheritance rather than composition only to establish order
/// of construction, since the public base class should be constructed after the
/// elements belonging to the private base class are constructed. This is not
/// possible using true composition.
class Server::UnimplementedAsyncRequest final
: private grpc::UnimplementedAsyncRequestContext,
public GenericAsyncRequest {
public:
UnimplementedAsyncRequest(ServerInterface* server,
grpc::ServerCompletionQueue* cq)
: GenericAsyncRequest(server, &server_context_, &generic_stream_, cq, cq,
/*tag=*/nullptr, /*delete_on_finalize=*/false,
/*issue_request=*/false) {
// Issue request here instead of the base class to prevent race on vptr.
IssueRequest();
}
bool FinalizeResult(void** tag, bool* status) override;
grpc::ServerContext* context() { return &server_context_; }
grpc::GenericServerAsyncReaderWriter* stream() { return &generic_stream_; }
};
/// UnimplementedAsyncResponse should not post user-visible completions to the
/// C++ completion queue, but is generated as a CQ event by the core
class Server::UnimplementedAsyncResponse final
: public grpc::internal::CallOpSet<
grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpServerSendStatus> {
public:
explicit UnimplementedAsyncResponse(UnimplementedAsyncRequest* request);
~UnimplementedAsyncResponse() override { delete request_; }
bool FinalizeResult(void** tag, bool* status) override {
if (grpc::internal::CallOpSet<
grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpServerSendStatus>::FinalizeResult(tag,
status)) {
delete this;
} else {
// The tag was swallowed due to interception. We will see it again.
}
return false;
}
private:
UnimplementedAsyncRequest* const request_;
};
class Server::SyncRequest final : public grpc::internal::CompletionQueueTag {
public:
SyncRequest(Server* server, grpc::internal::RpcServiceMethod* method,
grpc_core::Server::RegisteredCallAllocation* data)
: SyncRequest(server, method) {
CommonSetup(data);
data->deadline = &deadline_;
data->optional_payload = has_request_payload_ ? &request_payload_ : nullptr;
}
SyncRequest(Server* server, grpc::internal::RpcServiceMethod* method,
grpc_core::Server::BatchCallAllocation* data)
: SyncRequest(server, method) {
CommonSetup(data);
call_details_ = new grpc_call_details;
grpc_call_details_init(call_details_);
data->details = call_details_;
}
~SyncRequest() override {
// The destructor should only cleanup those objects created in the
// constructor, since some paths may or may not actually go through the
// Run stage where other objects are allocated.
if (has_request_payload_ && request_payload_) {
grpc_byte_buffer_destroy(request_payload_);
}
if (call_details_ != nullptr) {
grpc_call_details_destroy(call_details_);
delete call_details_;
}
grpc_metadata_array_destroy(&request_metadata_);
server_->UnrefWithPossibleNotify();
}
bool FinalizeResult(void** /*tag*/, bool* status) override {
if (!*status) {
delete this;
return false;
}
if (call_details_) {
deadline_ = call_details_->deadline;
}
return true;
}
void Run(const std::shared_ptr<GlobalCallbacks>& global_callbacks,
bool resources) {
ctx_.Init(deadline_, &request_metadata_);
wrapped_call_.Init(
call_, server_, &cq_, server_->max_receive_message_size(),
ctx_->ctx.set_server_rpc_info(method_->name(), method_->method_type(),
server_->interceptor_creators_));
ctx_->ctx.set_call(call_, server_->call_metric_recording_enabled(),
server_->server_metric_recorder());
ctx_->ctx.cq_ = &cq_;
request_metadata_.count = 0;
global_callbacks_ = global_callbacks;
resources_ = resources;
interceptor_methods_.SetCall(&*wrapped_call_);
interceptor_methods_.SetReverse();
// Set interception point for RECV INITIAL METADATA
interceptor_methods_.AddInterceptionHookPoint(
grpc::experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA);
interceptor_methods_.SetRecvInitialMetadata(&ctx_->ctx.client_metadata_);
if (has_request_payload_) {
// Set interception point for RECV MESSAGE
auto* handler = resources_ ? method_->handler()
: server_->resource_exhausted_handler_.get();
deserialized_request_ = handler->Deserialize(call_, request_payload_,
&request_status_, nullptr);
if (!request_status_.ok()) {
gpr_log(GPR_DEBUG, "Failed to deserialize message.");
}
request_payload_ = nullptr;
interceptor_methods_.AddInterceptionHookPoint(
grpc::experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
interceptor_methods_.SetRecvMessage(deserialized_request_, nullptr);
}
if (interceptor_methods_.RunInterceptors(
[this]() { ContinueRunAfterInterception(); })) {
ContinueRunAfterInterception();
} else {
// There were interceptors to be run, so ContinueRunAfterInterception
// will be run when interceptors are done.
}
}
void ContinueRunAfterInterception() {
ctx_->ctx.BeginCompletionOp(&*wrapped_call_, nullptr, nullptr);
global_callbacks_->PreSynchronousRequest(&ctx_->ctx);
auto* handler = resources_ ? method_->handler()
: server_->resource_exhausted_handler_.get();
handler->RunHandler(grpc::internal::MethodHandler::HandlerParameter(
&*wrapped_call_, &ctx_->ctx, deserialized_request_, request_status_,
nullptr, nullptr));
global_callbacks_->PostSynchronousRequest(&ctx_->ctx);
cq_.Shutdown();
grpc::internal::CompletionQueueTag* op_tag = ctx_->ctx.GetCompletionOpTag();
cq_.TryPluck(op_tag, gpr_inf_future(GPR_CLOCK_REALTIME));
// Ensure the cq_ is shutdown
grpc::PhonyTag ignored_tag;
GPR_ASSERT(cq_.Pluck(&ignored_tag) == false);
// Cleanup structures allocated during Run/ContinueRunAfterInterception
wrapped_call_.Destroy();
ctx_.Destroy();
delete this;
}
// For requests that must be only cleaned up but not actually Run
void Cleanup() {
cq_.Shutdown();
grpc_call_unref(call_);
delete this;
}
private:
SyncRequest(Server* server, grpc::internal::RpcServiceMethod* method)
: server_(server),
method_(method),
has_request_payload_(method->method_type() ==
grpc::internal::RpcMethod::NORMAL_RPC ||
method->method_type() ==
grpc::internal::RpcMethod::SERVER_STREAMING),
cq_(grpc_completion_queue_create_for_pluck(nullptr)) {}
template <class CallAllocation>
void CommonSetup(CallAllocation* data) {
server_->Ref();
grpc_metadata_array_init(&request_metadata_);
data->tag = static_cast<void*>(this);
data->call = &call_;
data->initial_metadata = &request_metadata_;
data->cq = cq_.cq();
}
Server* const server_;
grpc::internal::RpcServiceMethod* const method_;
const bool has_request_payload_;
grpc_call* call_;
grpc_call_details* call_details_ = nullptr;
gpr_timespec deadline_;
grpc_metadata_array request_metadata_;
grpc_byte_buffer* request_payload_ = nullptr;
grpc::CompletionQueue cq_;
grpc::Status request_status_;
std::shared_ptr<GlobalCallbacks> global_callbacks_;
bool resources_;
void* deserialized_request_ = nullptr;
grpc::internal::InterceptorBatchMethodsImpl interceptor_methods_;
// ServerContextWrapper allows ManualConstructor while using a private
// contructor of ServerContext via this friend class.
struct ServerContextWrapper {
ServerContext ctx;
ServerContextWrapper(gpr_timespec deadline, grpc_metadata_array* arr)
: ctx(deadline, arr) {}
};
grpc_core::ManualConstructor<ServerContextWrapper> ctx_;
grpc_core::ManualConstructor<internal::Call> wrapped_call_;
};
template <class ServerContextType>
class Server::CallbackRequest final
: public grpc::internal::CompletionQueueTag {
public:
static_assert(
std::is_base_of<grpc::CallbackServerContext, ServerContextType>::value,
"ServerContextType must be derived from CallbackServerContext");
// For codegen services, the value of method represents the defined
// characteristics of the method being requested. For generic services, method
// is nullptr since these services don't have pre-defined methods.
CallbackRequest(Server* server, grpc::internal::RpcServiceMethod* method,
grpc::CompletionQueue* cq,
grpc_core::Server::RegisteredCallAllocation* data)
: server_(server),
method_(method),
has_request_payload_(method->method_type() ==
grpc::internal::RpcMethod::NORMAL_RPC ||
method->method_type() ==
grpc::internal::RpcMethod::SERVER_STREAMING),
cq_(cq),
tag_(this),
ctx_(server_->context_allocator() != nullptr
? server_->context_allocator()->NewCallbackServerContext()
: nullptr) {
CommonSetup(server, data);
data->deadline = &deadline_;
data->optional_payload = has_request_payload_ ? &request_payload_ : nullptr;
}
// For generic services, method is nullptr since these services don't have
// pre-defined methods.
CallbackRequest(Server* server, grpc::CompletionQueue* cq,
grpc_core::Server::BatchCallAllocation* data)
: server_(server),
method_(nullptr),
has_request_payload_(false),
call_details_(new grpc_call_details),
cq_(cq),
tag_(this),
ctx_(server_->context_allocator() != nullptr
? server_->context_allocator()
->NewGenericCallbackServerContext()
: nullptr) {
CommonSetup(server, data);
grpc_call_details_init(call_details_);
data->details = call_details_;
}
~CallbackRequest() override {
delete call_details_;
grpc_metadata_array_destroy(&request_metadata_);
if (has_request_payload_ && request_payload_) {
grpc_byte_buffer_destroy(request_payload_);
}
if (ctx_alloc_by_default_ || server_->context_allocator() == nullptr) {
default_ctx_.Destroy();
}
server_->UnrefWithPossibleNotify();
}
// Needs specialization to account for different processing of metadata
// in generic API
bool FinalizeResult(void** tag, bool* status) override;
private:
// method_name needs to be specialized between named method and generic
const char* method_name() const;
class CallbackCallTag : public grpc_completion_queue_functor {
public:
explicit CallbackCallTag(Server::CallbackRequest<ServerContextType>* req)
: req_(req) {
functor_run = &CallbackCallTag::StaticRun;
// Set inlineable to true since this callback is internally-controlled
// without taking any locks, and thus does not need to be run from the
// executor (which triggers a thread hop). This should only be used by
// internal callbacks like this and not by user application code. The work
// here is actually non-trivial, but there is no chance of having user
// locks conflict with each other so it's ok to run inlined.
inlineable = true;
}
// force_run can not be performed on a tag if operations using this tag
// have been sent to PerformOpsOnCall. It is intended for error conditions
// that are detected before the operations are internally processed.
void force_run(bool ok) { Run(ok); }
private:
Server::CallbackRequest<ServerContextType>* req_;
grpc::internal::Call* call_;
static void StaticRun(grpc_completion_queue_functor* cb, int ok) {
static_cast<CallbackCallTag*>(cb)->Run(static_cast<bool>(ok));
}
void Run(bool ok) {
void* ignored = req_;
bool new_ok = ok;
GPR_ASSERT(!req_->FinalizeResult(&ignored, &new_ok));
GPR_ASSERT(ignored == req_);
if (!ok) {
// The call has been shutdown.
// Delete its contents to free up the request.
delete req_;
return;
}
// Bind the call, deadline, and metadata from what we got
req_->ctx_->set_call(req_->call_,
req_->server_->call_metric_recording_enabled(),
req_->server_->server_metric_recorder());
req_->ctx_->cq_ = req_->cq_;
req_->ctx_->BindDeadlineAndMetadata(req_->deadline_,
&req_->request_metadata_);
req_->request_metadata_.count = 0;
// Create a C++ Call to control the underlying core call
call_ =
new (grpc_call_arena_alloc(req_->call_, sizeof(grpc::internal::Call)))
grpc::internal::Call(
req_->call_, req_->server_, req_->cq_,
req_->server_->max_receive_message_size(),
req_->ctx_->set_server_rpc_info(
req_->method_name(),
(req_->method_ != nullptr)
? req_->method_->method_type()
: grpc::internal::RpcMethod::BIDI_STREAMING,
req_->server_->interceptor_creators_));
req_->interceptor_methods_.SetCall(call_);
req_->interceptor_methods_.SetReverse();
// Set interception point for RECV INITIAL METADATA
req_->interceptor_methods_.AddInterceptionHookPoint(
grpc::experimental::InterceptionHookPoints::
POST_RECV_INITIAL_METADATA);
req_->interceptor_methods_.SetRecvInitialMetadata(
&req_->ctx_->client_metadata_);
if (req_->has_request_payload_) {
// Set interception point for RECV MESSAGE
req_->request_ = req_->method_->handler()->Deserialize(
req_->call_, req_->request_payload_, &req_->request_status_,
&req_->handler_data_);
if (!(req_->request_status_.ok())) {
gpr_log(GPR_DEBUG, "Failed to deserialize message.");
}
req_->request_payload_ = nullptr;
req_->interceptor_methods_.AddInterceptionHookPoint(
grpc::experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
req_->interceptor_methods_.SetRecvMessage(req_->request_, nullptr);
}
if (req_->interceptor_methods_.RunInterceptors(
[this] { ContinueRunAfterInterception(); })) {
ContinueRunAfterInterception();
} else {
// There were interceptors to be run, so ContinueRunAfterInterception
// will be run when interceptors are done.
}
}
void ContinueRunAfterInterception() {
auto* handler = (req_->method_ != nullptr)
? req_->method_->handler()
: req_->server_->generic_handler_.get();
handler->RunHandler(grpc::internal::MethodHandler::HandlerParameter(
call_, req_->ctx_, req_->request_, req_->request_status_,
req_->handler_data_, [this] { delete req_; }));
}
};
template <class CallAllocation>
void CommonSetup(Server* server, CallAllocation* data) {
server->Ref();
grpc_metadata_array_init(&request_metadata_);
data->tag = static_cast<void*>(&tag_);
data->call = &call_;
data->initial_metadata = &request_metadata_;
if (ctx_ == nullptr) {
default_ctx_.Init();
ctx_ = &*default_ctx_;
ctx_alloc_by_default_ = true;
}
ctx_->set_context_allocator(server->context_allocator());
data->cq = cq_->cq();
}
Server* const server_;
grpc::internal::RpcServiceMethod* const method_;
const bool has_request_payload_;
grpc_byte_buffer* request_payload_ = nullptr;
void* request_ = nullptr;
void* handler_data_ = nullptr;
grpc::Status request_status_;
grpc_call_details* const call_details_ = nullptr;
grpc_call* call_;
gpr_timespec deadline_;
grpc_metadata_array request_metadata_;
grpc::CompletionQueue* const cq_;
bool ctx_alloc_by_default_ = false;
CallbackCallTag tag_;
ServerContextType* ctx_ = nullptr;
grpc_core::ManualConstructor<ServerContextType> default_ctx_;
grpc::internal::InterceptorBatchMethodsImpl interceptor_methods_;
};
template <>
bool Server::CallbackRequest<grpc::CallbackServerContext>::FinalizeResult(
void** /*tag*/, bool* /*status*/) {
return false;
}
template <>
bool Server::CallbackRequest<
grpc::GenericCallbackServerContext>::FinalizeResult(void** /*tag*/,
bool* status) {
if (*status) {
deadline_ = call_details_->deadline;
// TODO(yangg) remove the copy here
ctx_->method_ = grpc::StringFromCopiedSlice(call_details_->method);
ctx_->host_ = grpc::StringFromCopiedSlice(call_details_->host);
}
grpc_slice_unref(call_details_->method);
grpc_slice_unref(call_details_->host);
return false;
}
template <>
const char* Server::CallbackRequest<grpc::CallbackServerContext>::method_name()
const {
return method_->name();
}
template <>
const char* Server::CallbackRequest<
grpc::GenericCallbackServerContext>::method_name() const {
return ctx_->method().c_str();
}
// Implementation of ThreadManager. Each instance of SyncRequestThreadManager
// manages a pool of threads that poll for incoming Sync RPCs and call the
// appropriate RPC handlers
class Server::SyncRequestThreadManager : public grpc::ThreadManager {
public:
SyncRequestThreadManager(Server* server, grpc::CompletionQueue* server_cq,
std::shared_ptr<GlobalCallbacks> global_callbacks,
grpc_resource_quota* rq, int min_pollers,
int max_pollers, int cq_timeout_msec)
: ThreadManager("SyncServer", rq, min_pollers, max_pollers),
server_(server),
server_cq_(server_cq),
cq_timeout_msec_(cq_timeout_msec),
global_callbacks_(std::move(global_callbacks)) {}
WorkStatus PollForWork(void** tag, bool* ok) override {
*tag = nullptr;
// TODO(ctiller): workaround for GPR_TIMESPAN based deadlines not working
// right now
gpr_timespec deadline =
gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
gpr_time_from_millis(cq_timeout_msec_, GPR_TIMESPAN));
switch (server_cq_->AsyncNext(tag, ok, deadline)) {
case grpc::CompletionQueue::TIMEOUT:
return TIMEOUT;
case grpc::CompletionQueue::SHUTDOWN:
return SHUTDOWN;
case grpc::CompletionQueue::GOT_EVENT:
return WORK_FOUND;
}
GPR_UNREACHABLE_CODE(return TIMEOUT);
}
void DoWork(void* tag, bool ok, bool resources) override {
(void)ok;
SyncRequest* sync_req = static_cast<SyncRequest*>(tag);
// Under the AllocatingRequestMatcher model we will never see an invalid tag
// here.
GPR_DEBUG_ASSERT(sync_req != nullptr);
GPR_DEBUG_ASSERT(ok);
sync_req->Run(global_callbacks_, resources);
}
void AddSyncMethod(grpc::internal::RpcServiceMethod* method, void* tag) {
grpc_core::Server::FromC(server_->server())
->SetRegisteredMethodAllocator(server_cq_->cq(), tag, [this, method] {
grpc_core::Server::RegisteredCallAllocation result;
new SyncRequest(server_, method, &result);
return result;
});
has_sync_method_ = true;
}
void AddUnknownSyncMethod() {
if (has_sync_method_) {
unknown_method_ = std::make_unique<grpc::internal::RpcServiceMethod>(
"unknown", grpc::internal::RpcMethod::BIDI_STREAMING,
new grpc::internal::UnknownMethodHandler(kUnknownRpcMethod));
grpc_core::Server::FromC(server_->server())
->SetBatchMethodAllocator(server_cq_->cq(), [this] {
grpc_core::Server::BatchCallAllocation result;
new SyncRequest(server_, unknown_method_.get(), &result);
return result;
});
}
}
void Shutdown() override {
ThreadManager::Shutdown();
server_cq_->Shutdown();
}
void Wait() override {
ThreadManager::Wait();
// Drain any pending items from the queue
void* tag;
bool ok;
while (server_cq_->Next(&tag, &ok)) {
// This problem can arise if the server CQ gets a request queued to it
// before it gets shutdown but then pulls it after shutdown.
static_cast<SyncRequest*>(tag)->Cleanup();
}
}
void Start() {
if (has_sync_method_) {
Initialize(); // ThreadManager's Initialize()
}
}
private:
Server* server_;
grpc::CompletionQueue* server_cq_;
int cq_timeout_msec_;
bool has_sync_method_ = false;
std::unique_ptr<grpc::internal::RpcServiceMethod> unknown_method_;
std::shared_ptr<Server::GlobalCallbacks> global_callbacks_;
};
Server::Server(
grpc::ChannelArguments* args,
std::shared_ptr<std::vector<std::unique_ptr<grpc::ServerCompletionQueue>>>
sync_server_cqs,
int min_pollers, int max_pollers, int sync_cq_timeout_msec,
std::vector<std::shared_ptr<grpc::internal::ExternalConnectionAcceptorImpl>>
acceptors,
grpc_server_config_fetcher* server_config_fetcher,
grpc_resource_quota* server_rq,
std::vector<
std::unique_ptr<grpc::experimental::ServerInterceptorFactoryInterface>>
interceptor_creators,
experimental::ServerMetricRecorder* server_metric_recorder)
: acceptors_(std::move(acceptors)),
interceptor_creators_(std::move(interceptor_creators)),
max_receive_message_size_(INT_MIN),
sync_server_cqs_(std::move(sync_server_cqs)),
started_(false),
shutdown_(false),
shutdown_notified_(false),
server_(nullptr),
server_initializer_(new ServerInitializer(this)),
health_check_service_disabled_(false),
server_metric_recorder_(server_metric_recorder) {
gpr_once_init(&grpc::g_once_init_callbacks, grpc::InitGlobalCallbacks);
global_callbacks_ = grpc::g_callbacks;
global_callbacks_->UpdateArguments(args);
if (sync_server_cqs_ != nullptr) {
bool default_rq_created = false;
if (server_rq == nullptr) {
server_rq = grpc_resource_quota_create("SyncServer-default-rq");
grpc_resource_quota_set_max_threads(server_rq,
DEFAULT_MAX_SYNC_SERVER_THREADS);
default_rq_created = true;
}
for (const auto& it : *sync_server_cqs_) {
sync_req_mgrs_.emplace_back(new SyncRequestThreadManager(
this, it.get(), global_callbacks_, server_rq, min_pollers,
max_pollers, sync_cq_timeout_msec));
}
if (default_rq_created) {
grpc_resource_quota_unref(server_rq);
}
}
for (auto& acceptor : acceptors_) {
acceptor->SetToChannelArgs(args);
}
grpc_channel_args channel_args;
args->SetChannelArgs(&channel_args);
for (size_t i = 0; i < channel_args.num_args; i++) {
if (0 == strcmp(channel_args.args[i].key,
grpc::kHealthCheckServiceInterfaceArg)) {
if (channel_args.args[i].value.pointer.p == nullptr) {
health_check_service_disabled_ = true;
} else {
health_check_service_.reset(
static_cast<grpc::HealthCheckServiceInterface*>(
channel_args.args[i].value.pointer.p));
}
}
if (0 ==
strcmp(channel_args.args[i].key, GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH)) {
max_receive_message_size_ = channel_args.args[i].value.integer;
}
if (0 == strcmp(channel_args.args[i].key,
GRPC_ARG_SERVER_CALL_METRIC_RECORDING)) {
call_metric_recording_enabled_ = channel_args.args[i].value.integer;
}
}
server_ = grpc_server_create(&channel_args, nullptr);
grpc_server_set_config_fetcher(server_, server_config_fetcher);
}
Server::~Server() {
{
grpc::internal::ReleasableMutexLock lock(&mu_);
if (started_ && !shutdown_) {
lock.Release();
Shutdown();
} else if (!started_) {
// Shutdown the completion queues
for (const auto& value : sync_req_mgrs_) {
value->Shutdown();
}
CompletionQueue* callback_cq =
callback_cq_.load(std::memory_order_relaxed);
if (callback_cq != nullptr) {
if (grpc_iomgr_run_in_background()) {
// gRPC-core provides the backing needed for the preferred CQ type
callback_cq->Shutdown();
} else {
CompletionQueue::ReleaseCallbackAlternativeCQ(callback_cq);
}
callback_cq_.store(nullptr, std::memory_order_release);
}
}
}
// Destroy health check service before we destroy the C server so that
// it does not call grpc_server_request_registered_call() after the C
// server has been destroyed.
health_check_service_.reset();
grpc_server_destroy(server_);
}
void Server::SetGlobalCallbacks(GlobalCallbacks* callbacks) {
GPR_ASSERT(!grpc::g_callbacks);
GPR_ASSERT(callbacks);
grpc::g_callbacks.reset(callbacks);
}
grpc_server* Server::c_server() { return server_; }
std::shared_ptr<grpc::Channel> Server::InProcessChannel(
const grpc::ChannelArguments& args) {
grpc_channel_args channel_args = args.c_channel_args();
return grpc::CreateChannelInternal(
"inproc", grpc_inproc_channel_create(server_, &channel_args, nullptr),
std::vector<std::unique_ptr<
grpc::experimental::ClientInterceptorFactoryInterface>>());
}
std::shared_ptr<grpc::Channel>
Server::experimental_type::InProcessChannelWithInterceptors(
const grpc::ChannelArguments& args,
std::vector<
std::unique_ptr<grpc::experimental::ClientInterceptorFactoryInterface>>
interceptor_creators) {
grpc_channel_args channel_args = args.c_channel_args();
return grpc::CreateChannelInternal(
"inproc",
grpc_inproc_channel_create(server_->server_, &channel_args, nullptr),
std::move(interceptor_creators));
}
static grpc_server_register_method_payload_handling PayloadHandlingForMethod(
grpc::internal::RpcServiceMethod* method) {
switch (method->method_type()) {
case grpc::internal::RpcMethod::NORMAL_RPC:
case grpc::internal::RpcMethod::SERVER_STREAMING:
return GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER;
case grpc::internal::RpcMethod::CLIENT_STREAMING:
case grpc::internal::RpcMethod::BIDI_STREAMING:
return GRPC_SRM_PAYLOAD_NONE;
}
GPR_UNREACHABLE_CODE(return GRPC_SRM_PAYLOAD_NONE;);
}
bool Server::RegisterService(const std::string* addr, grpc::Service* service) {
bool has_async_methods = service->has_async_methods();
if (has_async_methods) {
GPR_ASSERT(service->server_ == nullptr &&
"Can only register an asynchronous service against one server.");
service->server_ = this;
}
const char* method_name = nullptr;
for (const auto& method : service->methods_) {
if (method == nullptr) { // Handled by generic service if any.
continue;
}
void* method_registration_tag = grpc_server_register_method(
server_, method->name(), addr ? addr->c_str() : nullptr,
PayloadHandlingForMethod(method.get()), 0);
if (method_registration_tag == nullptr) {
gpr_log(GPR_DEBUG, "Attempt to register %s multiple times",
method->name());
return false;
}
if (method->handler() == nullptr) { // Async method without handler
method->set_server_tag(method_registration_tag);
} else if (method->api_type() ==
grpc::internal::RpcServiceMethod::ApiType::SYNC) {
for (const auto& value : sync_req_mgrs_) {
value->AddSyncMethod(method.get(), method_registration_tag);
}
} else {
has_callback_methods_ = true;
grpc::internal::RpcServiceMethod* method_value = method.get();
grpc::CompletionQueue* cq = CallbackCQ();
grpc_server_register_completion_queue(server_, cq->cq(), nullptr);
grpc_core::Server::FromC(server_)->SetRegisteredMethodAllocator(
cq->cq(), method_registration_tag, [this, cq, method_value] {
grpc_core::Server::RegisteredCallAllocation result;
new CallbackRequest<grpc::CallbackServerContext>(this, method_value,
cq, &result);
return result;
});
}
method_name = method->name();
}
// Parse service name.
if (method_name != nullptr) {
std::stringstream ss(method_name);
std::string service_name;
if (std::getline(ss, service_name, '/') &&
std::getline(ss, service_name, '/')) {
services_.push_back(service_name);
}
}
return true;
}
void Server::RegisterAsyncGenericService(grpc::AsyncGenericService* service) {
GPR_ASSERT(service->server_ == nullptr &&
"Can only register an async generic service against one server.");
service->server_ = this;
has_async_generic_service_ = true;
}
void Server::RegisterCallbackGenericService(
grpc::CallbackGenericService* service) {
GPR_ASSERT(
service->server_ == nullptr &&
"Can only register a callback generic service against one server.");
service->server_ = this;
has_callback_generic_service_ = true;
generic_handler_.reset(service->Handler());
grpc::CompletionQueue* cq = CallbackCQ();
grpc_core::Server::FromC(server_)->SetBatchMethodAllocator(cq->cq(), [this,
cq] {
grpc_core::Server::BatchCallAllocation result;
new CallbackRequest<grpc::GenericCallbackServerContext>(this, cq, &result);
return result;
});
}
int Server::AddListeningPort(const std::string& addr,
grpc::ServerCredentials* creds) {
GPR_ASSERT(!started_);
int port = creds->AddPortToServer(addr, server_);
global_callbacks_->AddPort(this, addr, creds, port);
return port;
}
void Server::Ref() {
shutdown_refs_outstanding_.fetch_add(1, std::memory_order_relaxed);
}
void Server::UnrefWithPossibleNotify() {
if (GPR_UNLIKELY(shutdown_refs_outstanding_.fetch_sub(
1, std::memory_order_acq_rel) == 1)) {
// No refs outstanding means that shutdown has been initiated and no more
// callback requests are outstanding.
grpc::internal::MutexLock lock(&mu_);
GPR_ASSERT(shutdown_);
shutdown_done_ = true;
shutdown_done_cv_.Signal();
}
}
void Server::UnrefAndWaitLocked() {
if (GPR_UNLIKELY(shutdown_refs_outstanding_.fetch_sub(
1, std::memory_order_acq_rel) == 1)) {
shutdown_done_ = true;
return; // no need to wait on CV since done condition already set
}
while (!shutdown_done_) {
shutdown_done_cv_.Wait(&mu_);
}
}
void Server::Start(grpc::ServerCompletionQueue** cqs, size_t num_cqs) {
GPR_ASSERT(!started_);
global_callbacks_->PreServerStart(this);
started_ = true;
// Only create default health check service when user did not provide an
// explicit one.
if (health_check_service_ == nullptr && !health_check_service_disabled_ &&
grpc::DefaultHealthCheckServiceEnabled()) {
auto default_hc_service = std::make_unique<DefaultHealthCheckService>();
auto* hc_service_impl = default_hc_service->GetHealthCheckService();
health_check_service_ = std::move(default_hc_service);
RegisterService(nullptr, hc_service_impl);
}
for (auto& acceptor : acceptors_) {
acceptor->GetCredentials()->AddPortToServer(acceptor->name(), server_);
}
#ifndef NDEBUG
for (size_t i = 0; i < num_cqs; i++) {
cq_list_.push_back(cqs[i]);
}
#endif
// We must have exactly one generic service to handle requests for
// unmatched method names (i.e., to return UNIMPLEMENTED for any RPC
// method for which we don't have a registered implementation). This
// service comes from one of the following places (first match wins):
// - If the application supplied a generic service via either the async
// or callback APIs, we use that.
// - If there are callback methods, register a callback generic service.
// - If there are sync methods, register a sync generic service.
// (This must be done before server start to initialize an
// AllocatingRequestMatcher.)
// - Otherwise (we have only async methods), we wait until the server
// is started and then start an UnimplementedAsyncRequest on each
// async CQ, so that the requests will be moved along by polling
// done in application threads.
bool unknown_rpc_needed =
!has_async_generic_service_ && !has_callback_generic_service_;
if (unknown_rpc_needed && has_callback_methods_) {
unimplemented_service_ = std::make_unique<grpc::CallbackGenericService>();
RegisterCallbackGenericService(unimplemented_service_.get());
unknown_rpc_needed = false;
}
if (unknown_rpc_needed && !sync_req_mgrs_.empty()) {
sync_req_mgrs_[0]->AddUnknownSyncMethod();
unknown_rpc_needed = false;
}
grpc_server_start(server_);
if (unknown_rpc_needed) {
for (size_t i = 0; i < num_cqs; i++) {
if (cqs[i]->IsFrequentlyPolled()) {
new UnimplementedAsyncRequest(this, cqs[i]);
}
}
unknown_rpc_needed = false;
}
// If this server has any support for synchronous methods (has any sync
// server CQs), make sure that we have a ResourceExhausted handler
// to deal with the case of thread exhaustion
if (sync_server_cqs_ != nullptr && !sync_server_cqs_->empty()) {
resource_exhausted_handler_ =
std::make_unique<grpc::internal::ResourceExhaustedHandler>(
kServerThreadpoolExhausted);
}
for (const auto& value : sync_req_mgrs_) {
value->Start();
}
for (auto& acceptor : acceptors_) {
acceptor->Start();
}
}
void Server::ShutdownInternal(gpr_timespec deadline) {
grpc::internal::MutexLock lock(&mu_);
if (shutdown_) {
return;
}
shutdown_ = true;
for (auto& acceptor : acceptors_) {
acceptor->Shutdown();
}
/// The completion queue to use for server shutdown completion notification
grpc::CompletionQueue shutdown_cq;
grpc::ShutdownTag shutdown_tag; // Phony shutdown tag
grpc_server_shutdown_and_notify(server_, shutdown_cq.cq(), &shutdown_tag);
shutdown_cq.Shutdown();
void* tag;
bool ok;
grpc::CompletionQueue::NextStatus status =
shutdown_cq.AsyncNext(&tag, &ok, deadline);
// If this timed out, it means we are done with the grace period for a clean
// shutdown. We should force a shutdown now by cancelling all inflight calls
if (status == grpc::CompletionQueue::NextStatus::TIMEOUT) {
grpc_server_cancel_all_calls(server_);
status =
shutdown_cq.AsyncNext(&tag, &ok, gpr_inf_future(GPR_CLOCK_MONOTONIC));
}
// Else in case of SHUTDOWN or GOT_EVENT, it means that the server has
// successfully shutdown
// Drop the shutdown ref and wait for all other refs to drop as well.
UnrefAndWaitLocked();
// Shutdown all ThreadManagers. This will try to gracefully stop all the
// threads in the ThreadManagers (once they process any inflight requests)
for (const auto& value : sync_req_mgrs_) {
value->Shutdown(); // ThreadManager's Shutdown()
}
// Wait for threads in all ThreadManagers to terminate
for (const auto& value : sync_req_mgrs_) {
value->Wait();
}
// Shutdown the callback CQ. The CQ is owned by its own shutdown tag, so it
// will delete itself at true shutdown.
CompletionQueue* callback_cq = callback_cq_.load(std::memory_order_relaxed);
if (callback_cq != nullptr) {
if (grpc_iomgr_run_in_background()) {
// gRPC-core provides the backing needed for the preferred CQ type
callback_cq->Shutdown();
} else {
CompletionQueue::ReleaseCallbackAlternativeCQ(callback_cq);
}
callback_cq_.store(nullptr, std::memory_order_release);
}
// Drain the shutdown queue (if the previous call to AsyncNext() timed out
// and we didn't remove the tag from the queue yet)
while (shutdown_cq.Next(&tag, &ok)) {
// Nothing to be done here. Just ignore ok and tag values
}
shutdown_notified_ = true;
shutdown_cv_.SignalAll();
#ifndef NDEBUG
// Unregister this server with the CQs passed into it by the user so that
// those can be checked for properly-ordered shutdown.
for (auto* cq : cq_list_) {
cq->UnregisterServer(this);
}
cq_list_.clear();
#endif
}
void Server::Wait() {
grpc::internal::MutexLock lock(&mu_);
while (started_ && !shutdown_notified_) {
shutdown_cv_.Wait(&mu_);
}
}
void Server::PerformOpsOnCall(grpc::internal::CallOpSetInterface* ops,
grpc::internal::Call* call) {
ops->FillOps(call);
}
bool Server::UnimplementedAsyncRequest::FinalizeResult(void** tag,
bool* status) {
if (GenericAsyncRequest::FinalizeResult(tag, status)) {
// We either had no interceptors run or we are done intercepting
if (*status) {
// Create a new request/response pair using the server and CQ values
// stored in this object's base class.
new UnimplementedAsyncRequest(server_, notification_cq_);
new UnimplementedAsyncResponse(this);
} else {
delete this;
}
} else {
// The tag was swallowed due to interception. We will see it again.
}
return false;
}
Server::UnimplementedAsyncResponse::UnimplementedAsyncResponse(
UnimplementedAsyncRequest* request)
: request_(request) {
grpc::Status status(grpc::StatusCode::UNIMPLEMENTED, kUnknownRpcMethod);
grpc::internal::UnknownMethodHandler::FillOps(request_->context(),
kUnknownRpcMethod, this);
request_->stream()->call_.PerformOps(this);
}
grpc::ServerInitializer* Server::initializer() {
return server_initializer_.get();
}
grpc::CompletionQueue* Server::CallbackCQ() {
// TODO(vjpai): Consider using a single global CQ for the default CQ
// if there is no explicit per-server CQ registered
CompletionQueue* callback_cq = callback_cq_.load(std::memory_order_acquire);
if (callback_cq != nullptr) {
return callback_cq;
}
// The callback_cq_ wasn't already set, so grab a lock and set it up exactly
// once for this server.
grpc::internal::MutexLock l(&mu_);
callback_cq = callback_cq_.load(std::memory_order_relaxed);
if (callback_cq != nullptr) {
return callback_cq;
}
if (grpc_iomgr_run_in_background()) {
// gRPC-core provides the backing needed for the preferred CQ type
auto* shutdown_callback = new grpc::ShutdownCallback;
callback_cq = new grpc::CompletionQueue(grpc_completion_queue_attributes{
GRPC_CQ_CURRENT_VERSION, GRPC_CQ_CALLBACK, GRPC_CQ_DEFAULT_POLLING,
shutdown_callback});
// Transfer ownership of the new cq to its own shutdown callback
shutdown_callback->TakeCQ(callback_cq);
} else {
// Otherwise we need to use the alternative CQ variant
callback_cq = CompletionQueue::CallbackAlternativeCQ();
}
callback_cq_.store(callback_cq, std::memory_order_release);
return callback_cq;
}
} // namespace grpc

View File

@@ -0,0 +1,421 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <assert.h>
#include <atomic>
#include <cstdlib>
#include <functional>
#include <map>
#include <memory>
#include <new>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include <grpc/compression.h>
#include <grpc/grpc.h>
#include <grpc/impl/compression_types.h>
#include <grpc/load_reporting.h>
#include <grpc/status.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include <grpcpp/completion_queue.h>
#include <grpcpp/ext/call_metric_recorder.h>
#include <grpcpp/ext/server_metric_recorder.h>
#include <grpcpp/impl/call.h>
#include <grpcpp/impl/call_op_set.h>
#include <grpcpp/impl/call_op_set_interface.h>
#include <grpcpp/impl/completion_queue_tag.h>
#include <grpcpp/impl/interceptor_common.h>
#include <grpcpp/impl/metadata_map.h>
#include <grpcpp/server_context.h>
#include <grpcpp/support/callback_common.h>
#include <grpcpp/support/interceptor.h>
#include <grpcpp/support/server_callback.h>
#include <grpcpp/support/server_interceptor.h>
#include <grpcpp/support/string_ref.h>
#include "src/core/lib/channel/context.h"
#include "src/core/lib/gprpp/crash.h"
#include "src/core/lib/gprpp/ref_counted.h"
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/surface/call.h"
#include "src/cpp/server/backend_metric_recorder.h"
namespace grpc {
// CompletionOp
class ServerContextBase::CompletionOp final
: public internal::CallOpSetInterface {
public:
// initial refs: one in the server context, one in the cq
// must ref the call before calling constructor and after deleting this
CompletionOp(internal::Call* call,
grpc::internal::ServerCallbackCall* callback_controller)
: call_(*call),
callback_controller_(callback_controller),
has_tag_(false),
tag_(nullptr),
core_cq_tag_(this),
refs_(2),
finalized_(false),
cancelled_(0),
done_intercepting_(false) {}
// CompletionOp isn't copyable or movable
CompletionOp(const CompletionOp&) = delete;
CompletionOp& operator=(const CompletionOp&) = delete;
CompletionOp(CompletionOp&&) = delete;
CompletionOp& operator=(CompletionOp&&) = delete;
~CompletionOp() override {
if (call_.server_rpc_info()) {
call_.server_rpc_info()->Unref();
}
}
void FillOps(internal::Call* call) override;
// This should always be arena allocated in the call, so override delete.
// But this class is not trivially destructible, so must actually call delete
// before allowing the arena to be freed
static void operator delete(void* /*ptr*/, std::size_t size) {
// Use size to avoid unused-parameter warning since assert seems to be
// compiled out and treated as unused in some gcc optimized versions.
(void)size;
assert(size == sizeof(CompletionOp));
}
// This operator should never be called as the memory should be freed as part
// of the arena destruction. It only exists to provide a matching operator
// delete to the operator new so that some compilers will not complain (see
// https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
// there are no tests catching the compiler warning.
static void operator delete(void*, void*) { assert(0); }
bool FinalizeResult(void** tag, bool* status) override;
bool CheckCancelled(CompletionQueue* cq) {
cq->TryPluck(this);
return CheckCancelledNoPluck();
}
bool CheckCancelledAsync() { return CheckCancelledNoPluck(); }
void set_tag(void* tag) {
has_tag_ = true;
tag_ = tag;
}
void set_core_cq_tag(void* core_cq_tag) { core_cq_tag_ = core_cq_tag; }
void* core_cq_tag() override { return core_cq_tag_; }
void Unref();
// This will be called while interceptors are run if the RPC is a hijacked
// RPC. This should set hijacking state for each of the ops.
void SetHijackingState() override {
// Servers don't allow hijacking
grpc_core::Crash("unreachable");
}
// Should be called after interceptors are done running
void ContinueFillOpsAfterInterception() override {}
// Should be called after interceptors are done running on the finalize result
// path
void ContinueFinalizeResultAfterInterception() override {
done_intercepting_ = true;
if (!has_tag_) {
// We don't have a tag to return.
Unref();
// Unref can delete this, so do not access anything from this afterward.
return;
}
// Start a phony op so that we can return the tag
GPR_ASSERT(grpc_call_start_batch(call_.call(), nullptr, 0, core_cq_tag_,
nullptr) == GRPC_CALL_OK);
}
private:
bool CheckCancelledNoPluck() {
grpc_core::MutexLock lock(&mu_);
return finalized_ ? (cancelled_ != 0) : false;
}
internal::Call call_;
grpc::internal::ServerCallbackCall* const callback_controller_;
bool has_tag_;
void* tag_;
void* core_cq_tag_;
grpc_core::RefCount refs_;
grpc_core::Mutex mu_;
bool finalized_;
int cancelled_; // This is an int (not bool) because it is passed to core
bool done_intercepting_;
internal::InterceptorBatchMethodsImpl interceptor_methods_;
};
void ServerContextBase::CompletionOp::Unref() {
if (refs_.Unref()) {
grpc_call* call = call_.call();
delete this;
grpc_call_unref(call);
}
}
void ServerContextBase::CompletionOp::FillOps(internal::Call* call) {
grpc_op ops;
ops.op = GRPC_OP_RECV_CLOSE_ON_SERVER;
ops.data.recv_close_on_server.cancelled = &cancelled_;
ops.flags = 0;
ops.reserved = nullptr;
interceptor_methods_.SetCall(&call_);
interceptor_methods_.SetReverse();
interceptor_methods_.SetCallOpSetInterface(this);
// The following call_start_batch is internally-generated so no need for an
// explanatory log on failure.
GPR_ASSERT(grpc_call_start_batch(call->call(), &ops, 1, core_cq_tag_,
nullptr) == GRPC_CALL_OK);
// No interceptors to run here
}
bool ServerContextBase::CompletionOp::FinalizeResult(void** tag, bool* status) {
// Decide whether to do the unref or call the cancel callback within the lock
bool do_unref = false;
bool has_tag = false;
bool call_cancel = false;
{
grpc_core::MutexLock lock(&mu_);
if (done_intercepting_) {
// We are done intercepting.
has_tag = has_tag_;
if (has_tag) {
*tag = tag_;
}
// Release the lock before unreffing as Unref may delete this object
do_unref = true;
} else {
finalized_ = true;
// If for some reason the incoming status is false, mark that as a
// cancellation.
// TODO(vjpai): does this ever happen?
if (!*status) {
cancelled_ = 1;
}
call_cancel = (cancelled_ != 0);
// Release the lock since we may call a callback and interceptors.
}
}
if (do_unref) {
Unref();
// Unref can delete this, so do not access anything from this afterward.
return has_tag;
}
if (call_cancel && callback_controller_ != nullptr) {
callback_controller_->MaybeCallOnCancel();
}
// Add interception point and run through interceptors
interceptor_methods_.AddInterceptionHookPoint(
experimental::InterceptionHookPoints::POST_RECV_CLOSE);
if (interceptor_methods_.RunInterceptors()) {
// No interceptors were run
bool has_tag = has_tag_;
if (has_tag) {
*tag = tag_;
}
Unref();
// Unref can delete this, so do not access anything from this afterward.
return has_tag;
}
// There are interceptors to be run. Return false for now.
return false;
}
// ServerContextBase body
ServerContextBase::ServerContextBase()
: deadline_(gpr_inf_future(GPR_CLOCK_REALTIME)) {}
ServerContextBase::ServerContextBase(gpr_timespec deadline,
grpc_metadata_array* arr)
: deadline_(deadline) {
std::swap(*client_metadata_.arr(), *arr);
}
void ServerContextBase::BindDeadlineAndMetadata(gpr_timespec deadline,
grpc_metadata_array* arr) {
deadline_ = deadline;
std::swap(*client_metadata_.arr(), *arr);
}
ServerContextBase::~ServerContextBase() {
if (completion_op_) {
completion_op_->Unref();
// Unref can delete completion_op_, so do not access it afterward.
}
if (rpc_info_) {
rpc_info_->Unref();
}
if (default_reactor_used_.load(std::memory_order_relaxed)) {
reinterpret_cast<Reactor*>(&default_reactor_)->~Reactor();
}
if (call_metric_recorder_ != nullptr) {
call_metric_recorder_->~CallMetricRecorder();
}
}
ServerContextBase::CallWrapper::~CallWrapper() {
if (call) {
// If the ServerContext is part of the call's arena, this could free the
// object itself.
grpc_call_unref(call);
}
}
void ServerContextBase::BeginCompletionOp(
internal::Call* call, std::function<void(bool)> callback,
grpc::internal::ServerCallbackCall* callback_controller) {
GPR_ASSERT(!completion_op_);
if (rpc_info_) {
rpc_info_->Ref();
}
grpc_call_ref(call->call());
completion_op_ =
new (grpc_call_arena_alloc(call->call(), sizeof(CompletionOp)))
CompletionOp(call, callback_controller);
if (callback_controller != nullptr) {
completion_tag_.Set(call->call(), std::move(callback), completion_op_,
true);
completion_op_->set_core_cq_tag(&completion_tag_);
completion_op_->set_tag(completion_op_);
} else if (has_notify_when_done_tag_) {
completion_op_->set_tag(async_notify_when_done_tag_);
}
call->PerformOps(completion_op_);
}
internal::CompletionQueueTag* ServerContextBase::GetCompletionOpTag() {
return static_cast<internal::CompletionQueueTag*>(completion_op_);
}
void ServerContextBase::AddInitialMetadata(const std::string& key,
const std::string& value) {
initial_metadata_.insert(std::make_pair(key, value));
}
void ServerContextBase::AddTrailingMetadata(const std::string& key,
const std::string& value) {
trailing_metadata_.insert(std::make_pair(key, value));
}
void ServerContextBase::TryCancel() const {
internal::CancelInterceptorBatchMethods cancel_methods;
if (rpc_info_) {
for (size_t i = 0; i < rpc_info_->interceptors_.size(); i++) {
rpc_info_->RunInterceptor(&cancel_methods, i);
}
}
grpc_call_error err =
grpc_call_cancel_with_status(call_.call, GRPC_STATUS_CANCELLED,
"Cancelled on the server side", nullptr);
if (err != GRPC_CALL_OK) {
gpr_log(GPR_ERROR, "TryCancel failed with: %d", err);
}
}
bool ServerContextBase::IsCancelled() const {
if (completion_tag_) {
// When using callback API, this result is always valid.
return marked_cancelled_.load(std::memory_order_acquire) ||
completion_op_->CheckCancelledAsync();
} else if (has_notify_when_done_tag_) {
// When using async API, the result is only valid
// if the tag has already been delivered at the completion queue
return completion_op_ && completion_op_->CheckCancelledAsync();
} else {
// when using sync API, the result is always valid
return marked_cancelled_.load(std::memory_order_acquire) ||
(completion_op_ && completion_op_->CheckCancelled(cq_));
}
}
void ServerContextBase::set_compression_algorithm(
grpc_compression_algorithm algorithm) {
compression_algorithm_ = algorithm;
const char* algorithm_name = nullptr;
if (!grpc_compression_algorithm_name(algorithm, &algorithm_name)) {
grpc_core::Crash(absl::StrFormat(
"Name for compression algorithm '%d' unknown.", algorithm));
}
GPR_ASSERT(algorithm_name != nullptr);
AddInitialMetadata(GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY, algorithm_name);
}
std::string ServerContextBase::peer() const {
std::string peer;
if (call_.call) {
char* c_peer = grpc_call_get_peer(call_.call);
peer = c_peer;
gpr_free(c_peer);
}
return peer;
}
const struct census_context* ServerContextBase::census_context() const {
return call_.call == nullptr ? nullptr
: grpc_census_call_get_context(call_.call);
}
void ServerContextBase::SetLoadReportingCosts(
const std::vector<std::string>& cost_data) {
if (call_.call == nullptr) return;
for (const auto& cost_datum : cost_data) {
AddTrailingMetadata(GRPC_LB_COST_MD_KEY, cost_datum);
}
}
void ServerContextBase::CreateCallMetricRecorder(
experimental::ServerMetricRecorder* server_metric_recorder) {
if (call_.call == nullptr) return;
GPR_ASSERT(call_metric_recorder_ == nullptr);
grpc_core::Arena* arena = grpc_call_get_arena(call_.call);
auto* backend_metric_state =
arena->New<BackendMetricState>(server_metric_recorder);
call_metric_recorder_ = backend_metric_state;
grpc_call_context_set(call_.call, GRPC_CONTEXT_BACKEND_METRIC_PROVIDER,
backend_metric_state, nullptr);
}
grpc::string_ref ServerContextBase::ExperimentalGetAuthority() const {
absl::string_view authority = grpc_call_server_authority(call_.call);
return grpc::string_ref(authority.data(), authority.size());
}
} // namespace grpc

View File

@@ -0,0 +1,37 @@
//
//
// Copyright 2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <grpc/grpc.h>
#include <grpc/grpc_posix.h>
#include <grpc/grpc_security.h>
#include <grpcpp/server.h>
#include <grpcpp/server_posix.h>
namespace grpc {
#ifdef GPR_SUPPORT_CHANNELS_FROM_FD
void AddInsecureChannelFromFd(grpc::Server* server, int fd) {
grpc_server_credentials* creds = grpc_insecure_server_credentials_create();
grpc_server_add_channel_from_fd(server->c_server(), fd, creds);
grpc_server_credentials_release(creds);
}
#endif // GPR_SUPPORT_CHANNELS_FROM_FD
} // namespace grpc

View File

@@ -0,0 +1,43 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#ifndef GRPC_SRC_CPP_SERVER_THREAD_POOL_INTERFACE_H
#define GRPC_SRC_CPP_SERVER_THREAD_POOL_INTERFACE_H
#include <functional>
namespace grpc {
// A thread pool interface for running callbacks.
class ThreadPoolInterface {
public:
virtual ~ThreadPoolInterface() {}
// Schedule the given callback for execution.
virtual void Add(const std::function<void()>& callback) = 0;
};
// Allows different codebases to use their own thread pool impls
typedef ThreadPoolInterface* (*CreateThreadPoolFunc)(void);
void SetCreateThreadPool(CreateThreadPoolFunc func);
ThreadPoolInterface* CreateDefaultThreadPool();
} // namespace grpc
#endif // GRPC_SRC_CPP_SERVER_THREAD_POOL_INTERFACE_H

View File

@@ -0,0 +1,43 @@
//
//
// Copyright 2023 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <grpc/grpc.h>
#include <grpc/impl/channel_arg_names.h>
#include <grpcpp/server_builder.h>
#include <grpcpp/support/channel_arguments.h>
#include <grpcpp/xds_server_builder.h>
#include "src/core/ext/xds/xds_enabled_server.h"
namespace grpc {
ChannelArguments XdsServerBuilder::BuildChannelArgs() {
ChannelArguments args = ServerBuilder::BuildChannelArgs();
if (drain_grace_time_ms_ >= 0) {
args.SetInt(GRPC_ARG_SERVER_CONFIG_CHANGE_DRAIN_GRACE_TIME_MS,
drain_grace_time_ms_);
}
args.SetInt(GRPC_ARG_XDS_ENABLED_SERVER, 1);
grpc_channel_args c_channel_args = args.c_channel_args();
grpc_server_config_fetcher* fetcher = grpc_server_config_fetcher_xds_create(
{OnServingStatusUpdate, notifier_}, &c_channel_args);
if (fetcher != nullptr) set_fetcher(fetcher);
return args;
}
} // namespace grpc

View File

@@ -0,0 +1,54 @@
//
//
// Copyright 2020 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <memory>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpc/support/log.h>
#include <grpcpp/security/server_credentials.h>
#include "src/cpp/server/secure_server_credentials.h"
namespace grpc {
std::shared_ptr<ServerCredentials> XdsServerCredentials(
const std::shared_ptr<ServerCredentials>& fallback_credentials) {
GPR_ASSERT(fallback_credentials != nullptr);
if (fallback_credentials->IsInsecure()) {
grpc_server_credentials* insecure_creds =
grpc_insecure_server_credentials_create();
auto xds_creds = std::make_shared<SecureServerCredentials>(
grpc_xds_server_credentials_create(insecure_creds));
grpc_server_credentials_release(insecure_creds);
return xds_creds;
}
return std::make_shared<SecureServerCredentials>(
grpc_xds_server_credentials_create(
fallback_credentials->AsSecureServerCredentials()->c_creds()));
}
namespace experimental {
std::shared_ptr<ServerCredentials> XdsServerCredentials(
const std::shared_ptr<ServerCredentials>& fallback_credentials) {
return grpc::XdsServerCredentials(fallback_credentials);
}
} // namespace experimental
} // namespace grpc

View File

@@ -0,0 +1,266 @@
//
//
// Copyright 2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include "src/cpp/thread_manager/thread_manager.h"
#include <climits>
#include "absl/strings/str_format.h"
#include <grpc/support/log.h>
#include "src/core/lib/gprpp/crash.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/gprpp/thd.h"
#include "src/core/lib/resource_quota/resource_quota.h"
namespace grpc {
ThreadManager::WorkerThread::WorkerThread(ThreadManager* thd_mgr)
: thd_mgr_(thd_mgr) {
// Make thread creation exclusive with respect to its join happening in
// ~WorkerThread().
thd_ = grpc_core::Thread(
"grpcpp_sync_server",
[](void* th) { static_cast<ThreadManager::WorkerThread*>(th)->Run(); },
this, &created_);
if (!created_) {
gpr_log(GPR_ERROR, "Could not create grpc_sync_server worker-thread");
}
}
void ThreadManager::WorkerThread::Run() {
thd_mgr_->MainWorkLoop();
thd_mgr_->MarkAsCompleted(this);
}
ThreadManager::WorkerThread::~WorkerThread() {
// Don't join until the thread is fully constructed.
thd_.Join();
}
ThreadManager::ThreadManager(const char*, grpc_resource_quota* resource_quota,
int min_pollers, int max_pollers)
: shutdown_(false),
thread_quota_(
grpc_core::ResourceQuota::FromC(resource_quota)->thread_quota()),
num_pollers_(0),
min_pollers_(min_pollers),
max_pollers_(max_pollers == -1 ? INT_MAX : max_pollers),
num_threads_(0),
max_active_threads_sofar_(0) {}
ThreadManager::~ThreadManager() {
{
grpc_core::MutexLock lock(&mu_);
GPR_ASSERT(num_threads_ == 0);
}
CleanupCompletedThreads();
}
void ThreadManager::Wait() {
grpc_core::MutexLock lock(&mu_);
while (num_threads_ != 0) {
shutdown_cv_.Wait(&mu_);
}
}
void ThreadManager::Shutdown() {
grpc_core::MutexLock lock(&mu_);
shutdown_ = true;
}
bool ThreadManager::IsShutdown() {
grpc_core::MutexLock lock(&mu_);
return shutdown_;
}
int ThreadManager::GetMaxActiveThreadsSoFar() {
grpc_core::MutexLock list_lock(&list_mu_);
return max_active_threads_sofar_;
}
void ThreadManager::MarkAsCompleted(WorkerThread* thd) {
{
grpc_core::MutexLock list_lock(&list_mu_);
completed_threads_.push_back(thd);
}
{
grpc_core::MutexLock lock(&mu_);
num_threads_--;
if (num_threads_ == 0) {
shutdown_cv_.Signal();
}
}
// Give a thread back to the resource quota
thread_quota_->Release(1);
}
void ThreadManager::CleanupCompletedThreads() {
std::list<WorkerThread*> completed_threads;
{
// swap out the completed threads list: allows other threads to clean up
// more quickly
grpc_core::MutexLock lock(&list_mu_);
completed_threads.swap(completed_threads_);
}
for (auto thd : completed_threads) delete thd;
}
void ThreadManager::Initialize() {
if (!thread_quota_->Reserve(min_pollers_)) {
grpc_core::Crash(absl::StrFormat(
"No thread quota available to even create the minimum required "
"polling threads (i.e %d). Unable to start the thread manager",
min_pollers_));
}
{
grpc_core::MutexLock lock(&mu_);
num_pollers_ = min_pollers_;
num_threads_ = min_pollers_;
max_active_threads_sofar_ = min_pollers_;
}
for (int i = 0; i < min_pollers_; i++) {
WorkerThread* worker = new WorkerThread(this);
GPR_ASSERT(worker->created()); // Must be able to create the minimum
worker->Start();
}
}
void ThreadManager::MainWorkLoop() {
while (true) {
void* tag;
bool ok;
WorkStatus work_status = PollForWork(&tag, &ok);
grpc_core::LockableAndReleasableMutexLock lock(&mu_);
// Reduce the number of pollers by 1 and check what happened with the poll
num_pollers_--;
bool done = false;
switch (work_status) {
case TIMEOUT:
// If we timed out and we have more pollers than we need (or we are
// shutdown), finish this thread
if (shutdown_ || num_pollers_ > max_pollers_) done = true;
break;
case SHUTDOWN:
// If the thread manager is shutdown, finish this thread
done = true;
break;
case WORK_FOUND:
// If we got work and there are now insufficient pollers and there is
// quota available to create a new thread, start a new poller thread
bool resource_exhausted = false;
if (!shutdown_ && num_pollers_ < min_pollers_) {
if (thread_quota_->Reserve(1)) {
// We can allocate a new poller thread
num_pollers_++;
num_threads_++;
if (num_threads_ > max_active_threads_sofar_) {
max_active_threads_sofar_ = num_threads_;
}
// Drop lock before spawning thread to avoid contention
lock.Release();
WorkerThread* worker = new WorkerThread(this);
if (worker->created()) {
worker->Start();
} else {
// Get lock again to undo changes to poller/thread counters.
grpc_core::MutexLock failure_lock(&mu_);
num_pollers_--;
num_threads_--;
resource_exhausted = true;
delete worker;
}
} else if (num_pollers_ > 0) {
// There is still at least some thread polling, so we can go on
// even though we are below the number of pollers that we would
// like to have (min_pollers_)
lock.Release();
} else {
// There are no pollers to spare and we couldn't allocate
// a new thread, so resources are exhausted!
lock.Release();
resource_exhausted = true;
}
} else {
// There are a sufficient number of pollers available so we can do
// the work and continue polling with our existing poller threads
lock.Release();
}
// Lock is always released at this point - do the application work
// or return resource exhausted if there is new work but we couldn't
// get a thread in which to do it.
DoWork(tag, ok, !resource_exhausted);
// Take the lock again to check post conditions
lock.Lock();
// If we're shutdown, we should finish at this point.
if (shutdown_) done = true;
break;
}
// If we decided to finish the thread, break out of the while loop
if (done) break;
// Otherwise go back to polling as long as it doesn't exceed max_pollers_
//
// **WARNING**:
// There is a possibility of threads thrashing here (i.e excessive thread
// shutdowns and creations than the ideal case). This happens if max_poller_
// count is small and the rate of incoming requests is also small. In such
// scenarios we can possibly configure max_pollers_ to a higher value and/or
// increase the cq timeout.
//
// However, not doing this check here and unconditionally incrementing
// num_pollers (and hoping that the system will eventually settle down) has
// far worse consequences i.e huge number of threads getting created to the
// point of thread-exhaustion. For example: if the incoming request rate is
// very high, all the polling threads will return very quickly from
// PollForWork() with WORK_FOUND. They all briefly decrement num_pollers_
// counter thereby possibly - and briefly - making it go below min_pollers;
// This will most likely result in the creation of a new poller since
// num_pollers_ dipped below min_pollers_.
//
// Now, If we didn't do the max_poller_ check here, all these threads will
// go back to doing PollForWork() and the whole cycle repeats (with a new
// thread being added in each cycle). Once the total number of threads in
// the system crosses a certain threshold (around ~1500), there is heavy
// contention on mutexes (the mu_ here or the mutexes in gRPC core like the
// pollset mutex) that makes DoWork() take longer to finish thereby causing
// new poller threads to be created even faster. This results in a thread
// avalanche.
if (num_pollers_ < max_pollers_) {
num_pollers_++;
} else {
break;
}
};
// This thread is exiting. Do some cleanup work i.e delete already completed
// worker threads
CleanupCompletedThreads();
// If we are here, either ThreadManager is shutting down or it already has
// enough threads.
}
} // namespace grpc

View File

@@ -0,0 +1,179 @@
//
//
// Copyright 2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#ifndef GRPC_SRC_CPP_THREAD_MANAGER_THREAD_MANAGER_H
#define GRPC_SRC_CPP_THREAD_MANAGER_THREAD_MANAGER_H
#include <list>
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/gprpp/thd.h"
#include "src/core/lib/resource_quota/api.h"
#include "src/core/lib/resource_quota/thread_quota.h"
namespace grpc {
class ThreadManager {
public:
explicit ThreadManager(const char* name, grpc_resource_quota* resource_quota,
int min_pollers, int max_pollers);
virtual ~ThreadManager();
// Initializes and Starts the Rpc Manager threads
void Initialize();
// The return type of PollForWork() function
enum WorkStatus { WORK_FOUND, SHUTDOWN, TIMEOUT };
// "Polls" for new work.
// If the return value is WORK_FOUND:
// - The implementaion of PollForWork() MAY set some opaque identifier to
// (identify the work item found) via the '*tag' parameter
// - The implementaion MUST set the value of 'ok' to 'true' or 'false'. A
// value of 'false' indicates some implemenation specific error (that is
// neither SHUTDOWN nor TIMEOUT)
// - ThreadManager does not interpret the values of 'tag' and 'ok'
// - ThreadManager WILL call DoWork() and pass '*tag' and 'ok' as input to
// DoWork()
//
// If the return value is SHUTDOWN:,
// - ThreadManager WILL NOT call DoWork() and terminates the thread
//
// If the return value is TIMEOUT:,
// - ThreadManager WILL NOT call DoWork()
// - ThreadManager MAY terminate the thread depending on the current number
// of active poller threads and mix_pollers/max_pollers settings
// - Also, the value of timeout is specific to the derived class
// implementation
virtual WorkStatus PollForWork(void** tag, bool* ok) = 0;
// The implementation of DoWork() is supposed to perform the work found by
// PollForWork(). The tag and ok parameters are the same as returned by
// PollForWork(). The resources parameter indicates that the call actually
// has the resources available for performing the RPC's work. If it doesn't,
// the implementation should fail it appropriately.
//
// The implementation of DoWork() should also do any setup needed to ensure
// that the next call to PollForWork() (not necessarily by the current thread)
// actually finds some work
virtual void DoWork(void* tag, bool ok, bool resources) = 0;
// Mark the ThreadManager as shutdown and begin draining the work. This is a
// non-blocking call and the caller should call Wait(), a blocking call which
// returns only once the shutdown is complete
virtual void Shutdown();
// Has Shutdown() been called
bool IsShutdown();
// A blocking call that returns only after the ThreadManager has shutdown and
// all the threads have drained all the outstanding work
virtual void Wait();
// Max number of concurrent threads that were ever active in this thread
// manager so far. This is useful for debugging purposes (and in unit tests)
// to check if resource_quota is properly being enforced.
int GetMaxActiveThreadsSoFar();
private:
// Helper wrapper class around grpc_core::Thread. Takes a ThreadManager object
// and starts a new grpc_core::Thread to calls the Run() function.
//
// The Run() function calls ThreadManager::MainWorkLoop() function and once
// that completes, it marks the WorkerThread completed by calling
// ThreadManager::MarkAsCompleted()
//
// WHY IS THIS NEEDED?:
// When a thread terminates, some other thread *must* call Join() on that
// thread so that the resources are released. Having a WorkerThread wrapper
// will make this easier. Once Run() completes, each thread calls the
// following two functions:
// ThreadManager::CleanupCompletedThreads()
// ThreadManager::MarkAsCompleted()
//
// - MarkAsCompleted() puts the WorkerThread object in the ThreadManger's
// completed_threads_ list
// - CleanupCompletedThreads() calls "Join()" on the threads that are already
// in the completed_threads_ list (since a thread cannot call Join() on
// itself, it calls CleanupCompletedThreads() *before* calling
// MarkAsCompleted())
//
// TODO(sreek): Consider creating the threads 'detached' so that Join() need
// not be called (and the need for this WorkerThread class is eliminated)
class WorkerThread {
public:
explicit WorkerThread(ThreadManager* thd_mgr);
~WorkerThread();
bool created() const { return created_; }
void Start() { thd_.Start(); }
private:
// Calls thd_mgr_->MainWorkLoop() and once that completes, calls
// thd_mgr_>MarkAsCompleted(this) to mark the thread as completed
void Run();
ThreadManager* const thd_mgr_;
grpc_core::Thread thd_;
bool created_;
};
// The main function in ThreadManager
void MainWorkLoop();
void MarkAsCompleted(WorkerThread* thd);
void CleanupCompletedThreads();
// Protects shutdown_, num_pollers_, num_threads_ and
// max_active_threads_sofar_
grpc_core::Mutex mu_;
bool shutdown_;
grpc_core::CondVar shutdown_cv_;
// The resource user object to use when requesting quota to create threads
//
// Note: The user of this ThreadManager object must create grpc_resource_quota
// object (that contains the actual max thread quota) and a grpc_resource_user
// object through which quota is requested whenever new threads need to be
// created
grpc_core::ThreadQuotaPtr thread_quota_;
// Number of threads doing polling
int num_pollers_;
// The minimum and maximum number of threads that should be doing polling
int min_pollers_;
int max_pollers_;
// The total number of threads currently active (includes threads includes the
// threads that are currently polling i.e num_pollers_)
int num_threads_;
// See GetMaxActiveThreadsSoFar()'s description.
// To be more specific, this variable tracks the max value num_threads_ was
// ever set so far
int max_active_threads_sofar_;
grpc_core::Mutex list_mu_;
std::list<WorkerThread*> completed_threads_;
};
} // namespace grpc
#endif // GRPC_SRC_CPP_THREAD_MANAGER_THREAD_MANAGER_H

View File

@@ -0,0 +1,82 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <algorithm>
#include <vector>
#include <grpc/byte_buffer.h>
#include <grpc/byte_buffer_reader.h>
#include <grpc/grpc.h>
#include <grpc/impl/compression_types.h>
#include <grpc/slice.h>
#include <grpcpp/support/byte_buffer.h>
#include <grpcpp/support/slice.h>
#include <grpcpp/support/status.h>
namespace grpc {
Status ByteBuffer::TrySingleSlice(Slice* slice) const {
if (!buffer_) {
return Status(StatusCode::FAILED_PRECONDITION, "Buffer not initialized");
}
if ((buffer_->type == GRPC_BB_RAW) &&
(buffer_->data.raw.compression == GRPC_COMPRESS_NONE) &&
(buffer_->data.raw.slice_buffer.count == 1)) {
grpc_slice internal_slice = buffer_->data.raw.slice_buffer.slices[0];
*slice = Slice(internal_slice, Slice::ADD_REF);
return Status::OK;
} else {
return Status(StatusCode::FAILED_PRECONDITION,
"Buffer isn't made up of a single uncompressed slice.");
}
}
Status ByteBuffer::DumpToSingleSlice(Slice* slice) const {
if (!buffer_) {
return Status(StatusCode::FAILED_PRECONDITION, "Buffer not initialized");
}
grpc_byte_buffer_reader reader;
if (!grpc_byte_buffer_reader_init(&reader, buffer_)) {
return Status(StatusCode::INTERNAL,
"Couldn't initialize byte buffer reader");
}
grpc_slice s = grpc_byte_buffer_reader_readall(&reader);
*slice = Slice(s, Slice::STEAL_REF);
grpc_byte_buffer_reader_destroy(&reader);
return Status::OK;
}
Status ByteBuffer::Dump(std::vector<Slice>* slices) const {
slices->clear();
if (!buffer_) {
return Status(StatusCode::FAILED_PRECONDITION, "Buffer not initialized");
}
grpc_byte_buffer_reader reader;
if (!grpc_byte_buffer_reader_init(&reader, buffer_)) {
return Status(StatusCode::INTERNAL,
"Couldn't initialize byte buffer reader");
}
grpc_slice s;
while (grpc_byte_buffer_reader_next(&reader, &s)) {
slices->push_back(Slice(s, Slice::STEAL_REF));
}
grpc_byte_buffer_reader_destroy(&reader);
return Status::OK;
}
} // namespace grpc

28
Pods/gRPC-C++/src/cpp/util/status.cc generated Normal file
View File

@@ -0,0 +1,28 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <memory>
#include <grpcpp/support/status.h>
namespace grpc {
const Status& Status::OK = Status();
const Status& Status::CANCELLED = Status(StatusCode::CANCELLED, "");
} // namespace grpc

27
Pods/gRPC-C++/src/cpp/util/string_ref.cc generated Normal file
View File

@@ -0,0 +1,27 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <stddef.h>
#include <grpcpp/support/string_ref.h>
namespace grpc {
const size_t string_ref::npos = static_cast<size_t>(-1);
} // namespace grpc

79
Pods/gRPC-C++/src/cpp/util/time_cc.cc generated Normal file
View File

@@ -0,0 +1,79 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <chrono>
#include <cstdint>
#include <grpc/support/time.h>
#include <grpcpp/support/time.h>
// IWYU pragma: no_include <ratio>
using std::chrono::duration_cast;
using std::chrono::high_resolution_clock;
using std::chrono::nanoseconds;
using std::chrono::seconds;
using std::chrono::system_clock;
namespace grpc {
void Timepoint2Timespec(const system_clock::time_point& from,
gpr_timespec* to) {
system_clock::duration deadline = from.time_since_epoch();
seconds secs = duration_cast<seconds>(deadline);
if (from == system_clock::time_point::max() ||
secs.count() >= gpr_inf_future(GPR_CLOCK_REALTIME).tv_sec ||
secs.count() < 0) {
*to = gpr_inf_future(GPR_CLOCK_REALTIME);
return;
}
nanoseconds nsecs = duration_cast<nanoseconds>(deadline - secs);
to->tv_sec = static_cast<int64_t>(secs.count());
to->tv_nsec = static_cast<int32_t>(nsecs.count());
to->clock_type = GPR_CLOCK_REALTIME;
}
void TimepointHR2Timespec(const high_resolution_clock::time_point& from,
gpr_timespec* to) {
high_resolution_clock::duration deadline = from.time_since_epoch();
seconds secs = duration_cast<seconds>(deadline);
if (from == high_resolution_clock::time_point::max() ||
secs.count() >= gpr_inf_future(GPR_CLOCK_REALTIME).tv_sec ||
secs.count() < 0) {
*to = gpr_inf_future(GPR_CLOCK_REALTIME);
return;
}
nanoseconds nsecs = duration_cast<nanoseconds>(deadline - secs);
to->tv_sec = static_cast<int64_t>(secs.count());
to->tv_nsec = static_cast<int32_t>(nsecs.count());
to->clock_type = GPR_CLOCK_REALTIME;
}
system_clock::time_point Timespec2Timepoint(gpr_timespec t) {
if (gpr_time_cmp(t, gpr_inf_future(t.clock_type)) == 0) {
return system_clock::time_point::max();
}
t = gpr_convert_clock_type(t, GPR_CLOCK_REALTIME);
system_clock::time_point tp;
tp += duration_cast<system_clock::time_point::duration>(seconds(t.tv_sec));
tp +=
duration_cast<system_clock::time_point::duration>(nanoseconds(t.tv_nsec));
return tp;
}
} // namespace grpc