#include "test/cpp/end2end/test_service_impl.h"
#include <string>
#include <thread>
#include <grpc/support/log.h>
#include <grpcpp/security/credentials.h>
#include <grpcpp/server_context.h>
#include "src/proto/grpc/testing/echo.grpc.pb.h"
#include "test/cpp/util/string_ref_helper.h"
#include <gtest/gtest.h>
using std::chrono::system_clock;
namespace grpc {
namespace testing {
namespace {
void MaybeEchoDeadline(ServerContext* context, const EchoRequest* request,
EchoResponse* response) {
if (request->has_param() && request->param().echo_deadline()) {
gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_REALTIME);
if (context->deadline() != system_clock::time_point::max()) {
Timepoint2Timespec(context->deadline(), &deadline);
}
response->mutable_param()->set_request_deadline(deadline.tv_sec);
}
}
void CheckServerAuthContext(
const ServerContext* context,
const grpc::string& expected_transport_security_type,
const grpc::string& expected_client_identity) {
std::shared_ptr<const AuthContext> auth_ctx = context->auth_context();
std::vector<grpc::string_ref> tst =
auth_ctx->FindPropertyValues("transport_security_type");
EXPECT_EQ(1u, tst.size());
EXPECT_EQ(expected_transport_security_type, ToString(tst[0]));
if (expected_client_identity.empty()) {
EXPECT_TRUE(auth_ctx->GetPeerIdentityPropertyName().empty());
EXPECT_TRUE(auth_ctx->GetPeerIdentity().empty());
EXPECT_FALSE(auth_ctx->IsPeerAuthenticated());
} else {
auto identity = auth_ctx->GetPeerIdentity();
EXPECT_TRUE(auth_ctx->IsPeerAuthenticated());
EXPECT_EQ(1u, identity.size());
EXPECT_EQ(expected_client_identity, identity[0]);
}
}
}
Status TestServiceImpl::Echo(ServerContext* context, const EchoRequest* request,
EchoResponse* response) {
if (request->has_param() && request->param().server_sleep_us() > 0) {
gpr_sleep_until(
gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
gpr_time_from_micros(request->param().server_sleep_us(),
GPR_TIMESPAN)));
}
if (request->has_param() && request->param().server_die()) {
gpr_log(GPR_ERROR, "The request should not reach application handler.");
GPR_ASSERT(0);
}
if (request->has_param() && request->param().has_expected_error()) {
const auto& error = request->param().expected_error();
return Status(static_cast<StatusCode>(error.code()), error.error_message(),
error.binary_error_details());
}
int server_try_cancel = GetIntValueFromMetadata(
kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
if (server_try_cancel > DO_NOT_CANCEL) {
ServerTryCancel(context);
return Status::CANCELLED;
}
response->set_message(request->message());
MaybeEchoDeadline(context, request, response);
if (host_) {
response->mutable_param()->set_host(*host_);
}
if (request->has_param() && request->param().client_cancel_after_us()) {
{
std::unique_lock<std::mutex> lock(mu_);
signal_client_ = true;
}
while (!context->IsCancelled()) {
gpr_sleep_until(gpr_time_add(
gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_micros(request->param().client_cancel_after_us(),
GPR_TIMESPAN)));
}
return Status::CANCELLED;
} else if (request->has_param() &&
request->param().server_cancel_after_us()) {
gpr_sleep_until(gpr_time_add(
gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_micros(request->param().server_cancel_after_us(),
GPR_TIMESPAN)));
return Status::CANCELLED;
} else if (!request->has_param() ||
!request->param().skip_cancelled_check()) {
EXPECT_FALSE(context->IsCancelled());
}
if (request->has_param() && request->param().echo_metadata()) {
const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata =
context->client_metadata();
for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator
iter = client_metadata.begin();
iter != client_metadata.end(); ++iter) {
context->AddTrailingMetadata(ToString(iter->first),
ToString(iter->second));
}
if (request->param().debug_info().stack_entries_size() ||
!request->param().debug_info().detail().empty()) {
grpc::string serialized_debug_info =
request->param().debug_info().SerializeAsString();
context->AddTrailingMetadata(kDebugInfoTrailerKey, serialized_debug_info);
return Status::CANCELLED;
}
}
if (request->has_param() &&
(request->param().expected_client_identity().length() > 0 ||
request->param().check_auth_context())) {
CheckServerAuthContext(context,
request->param().expected_transport_security_type(),
request->param().expected_client_identity());
}
if (request->has_param() && request->param().response_message_length() > 0) {
response->set_message(
grpc::string(request->param().response_message_length(), '\0'));
}
if (request->has_param() && request->param().echo_peer()) {
response->mutable_param()->set_peer(context->peer());
}
return Status::OK;
}
void CallbackTestServiceImpl::Echo(
ServerContext* context, const EchoRequest* request, EchoResponse* response,
experimental::ServerCallbackRpcController* controller) {
if (request->has_param() && request->param().server_sleep_us() > 0) {
alarm_.experimental().Set(
gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
gpr_time_from_micros(request->param().server_sleep_us(),
GPR_TIMESPAN)),
[this, context, request, response, controller](bool) {
EchoNonDelayed(context, request, response, controller);
});
} else {
EchoNonDelayed(context, request, response, controller);
}
}
void CallbackTestServiceImpl::EchoNonDelayed(
ServerContext* context, const EchoRequest* request, EchoResponse* response,
experimental::ServerCallbackRpcController* controller) {
if (request->has_param() && request->param().server_die()) {
gpr_log(GPR_ERROR, "The request should not reach application handler.");
GPR_ASSERT(0);
}
if (request->has_param() && request->param().has_expected_error()) {
const auto& error = request->param().expected_error();
controller->Finish(Status(static_cast<StatusCode>(error.code()),
error.error_message(),
error.binary_error_details()));
}
int server_try_cancel = GetIntValueFromMetadata(
kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
if (server_try_cancel > DO_NOT_CANCEL) {
EXPECT_FALSE(context->IsCancelled());
context->TryCancel();
gpr_log(GPR_INFO, "Server called TryCancel() to cancel the request");
std::function<void(bool)> recurrence = [this, context, controller,
&recurrence](bool) {
if (!context->IsCancelled()) {
alarm_.experimental().Set(
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_micros(1000, GPR_TIMESPAN)),
recurrence);
} else {
controller->Finish(Status::CANCELLED);
}
};
recurrence(true);
return;
}
response->set_message(request->message());
MaybeEchoDeadline(context, request, response);
if (host_) {
response->mutable_param()->set_host(*host_);
}
if (request->has_param() && request->param().client_cancel_after_us()) {
{
std::unique_lock<std::mutex> lock(mu_);
signal_client_ = true;
}
std::function<void(bool)> recurrence = [this, context, request, controller,
&recurrence](bool) {
if (!context->IsCancelled()) {
alarm_.experimental().Set(
gpr_time_add(
gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_micros(request->param().client_cancel_after_us(),
GPR_TIMESPAN)),
recurrence);
} else {
controller->Finish(Status::CANCELLED);
}
};
recurrence(true);
return;
} else if (request->has_param() &&
request->param().server_cancel_after_us()) {
alarm_.experimental().Set(
gpr_time_add(
gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_micros(request->param().client_cancel_after_us(),
GPR_TIMESPAN)),
[controller](bool) { controller->Finish(Status::CANCELLED); });
return;
} else if (!request->has_param() ||
!request->param().skip_cancelled_check()) {
EXPECT_FALSE(context->IsCancelled());
}
if (request->has_param() && request->param().echo_metadata()) {
const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata =
context->client_metadata();
for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator
iter = client_metadata.begin();
iter != client_metadata.end(); ++iter) {
context->AddTrailingMetadata(ToString(iter->first),
ToString(iter->second));
}
if (request->param().debug_info().stack_entries_size() ||
!request->param().debug_info().detail().empty()) {
grpc::string serialized_debug_info =
request->param().debug_info().SerializeAsString();
context->AddTrailingMetadata(kDebugInfoTrailerKey, serialized_debug_info);
controller->Finish(Status::CANCELLED);
}
}
if (request->has_param() &&
(request->param().expected_client_identity().length() > 0 ||
request->param().check_auth_context())) {
CheckServerAuthContext(context,
request->param().expected_transport_security_type(),
request->param().expected_client_identity());
}
if (request->has_param() && request->param().response_message_length() > 0) {
response->set_message(
grpc::string(request->param().response_message_length(), '\0'));
}
if (request->has_param() && request->param().echo_peer()) {
response->mutable_param()->set_peer(context->peer());
}
controller->Finish(Status::OK);
}
Status TestServiceImpl::RequestStream(ServerContext* context,
ServerReader<EchoRequest>* reader,
EchoResponse* response) {
int server_try_cancel = GetIntValueFromMetadata(
kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
EchoRequest request;
response->set_message("");
if (server_try_cancel == CANCEL_BEFORE_PROCESSING) {
ServerTryCancel(context);
return Status::CANCELLED;
}
std::thread* server_try_cancel_thd = nullptr;
if (server_try_cancel == CANCEL_DURING_PROCESSING) {
server_try_cancel_thd =
new std::thread(&TestServiceImpl::ServerTryCancel, this, context);
}
int num_msgs_read = 0;
while (reader->Read(&request)) {
response->mutable_message()->append(request.message());
}
gpr_log(GPR_INFO, "Read: %d messages", num_msgs_read);
if (server_try_cancel_thd != nullptr) {
server_try_cancel_thd->join();
delete server_try_cancel_thd;
return Status::CANCELLED;
}
if (server_try_cancel == CANCEL_AFTER_PROCESSING) {
ServerTryCancel(context);
return Status::CANCELLED;
}
return Status::OK;
}
Status TestServiceImpl::ResponseStream(ServerContext* context,
const EchoRequest* request,
ServerWriter<EchoResponse>* writer) {
int server_try_cancel = GetIntValueFromMetadata(
kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
int server_coalescing_api = GetIntValueFromMetadata(
kServerUseCoalescingApi, context->client_metadata(), 0);
int server_responses_to_send = GetIntValueFromMetadata(
kServerResponseStreamsToSend, context->client_metadata(),
kServerDefaultResponseStreamsToSend);
if (server_try_cancel == CANCEL_BEFORE_PROCESSING) {
ServerTryCancel(context);
return Status::CANCELLED;
}
EchoResponse response;
std::thread* server_try_cancel_thd = nullptr;
if (server_try_cancel == CANCEL_DURING_PROCESSING) {
server_try_cancel_thd =
new std::thread(&TestServiceImpl::ServerTryCancel, this, context);
}
for (int i = 0; i < server_responses_to_send; i++) {
response.set_message(request->message() + grpc::to_string(i));
if (i == server_responses_to_send - 1 && server_coalescing_api != 0) {
writer->WriteLast(response, WriteOptions());
} else {
writer->Write(response);
}
}
if (server_try_cancel_thd != nullptr) {
server_try_cancel_thd->join();
delete server_try_cancel_thd;
return Status::CANCELLED;
}
if (server_try_cancel == CANCEL_AFTER_PROCESSING) {
ServerTryCancel(context);
return Status::CANCELLED;
}
return Status::OK;
}
Status TestServiceImpl::BidiStream(
ServerContext* context,
ServerReaderWriter<EchoResponse, EchoRequest>* stream) {
int server_try_cancel = GetIntValueFromMetadata(
kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
EchoRequest request;
EchoResponse response;
if (server_try_cancel == CANCEL_BEFORE_PROCESSING) {
ServerTryCancel(context);
return Status::CANCELLED;
}
std::thread* server_try_cancel_thd = nullptr;
if (server_try_cancel == CANCEL_DURING_PROCESSING) {
server_try_cancel_thd =
new std::thread(&TestServiceImpl::ServerTryCancel, this, context);
}
int server_write_last = GetIntValueFromMetadata(
kServerFinishAfterNReads, context->client_metadata(), 0);
int read_counts = 0;
while (stream->Read(&request)) {
read_counts++;
gpr_log(GPR_INFO, "recv msg %s", request.message().c_str());
response.set_message(request.message());
if (read_counts == server_write_last) {
stream->WriteLast(response, WriteOptions());
} else {
stream->Write(response);
}
}
if (server_try_cancel_thd != nullptr) {
server_try_cancel_thd->join();
delete server_try_cancel_thd;
return Status::CANCELLED;
}
if (server_try_cancel == CANCEL_AFTER_PROCESSING) {
ServerTryCancel(context);
return Status::CANCELLED;
}
return Status::OK;
}
namespace {
int GetIntValueFromMetadataHelper(
const char* key,
const std::multimap<grpc::string_ref, grpc::string_ref>& metadata,
int default_value) {
if (metadata.find(key) != metadata.end()) {
std::istringstream iss(ToString(metadata.find(key)->second));
iss >> default_value;
gpr_log(GPR_INFO, "%s : %d", key, default_value);
}
return default_value;
}
};
int TestServiceImpl::GetIntValueFromMetadata(
const char* key,
const std::multimap<grpc::string_ref, grpc::string_ref>& metadata,
int default_value) {
return GetIntValueFromMetadataHelper(key, metadata, default_value);
}
int CallbackTestServiceImpl::GetIntValueFromMetadata(
const char* key,
const std::multimap<grpc::string_ref, grpc::string_ref>& metadata,
int default_value) {
return GetIntValueFromMetadataHelper(key, metadata, default_value);
}
void TestServiceImpl::ServerTryCancel(ServerContext* context) {
EXPECT_FALSE(context->IsCancelled());
context->TryCancel();
gpr_log(GPR_INFO, "Server called TryCancel() to cancel the request");
while (!context->IsCancelled()) {
gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_micros(1000, GPR_TIMESPAN)));
}
}
} }