mirror of https://github.com/grpc/grpc.git
The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)
https://grpc.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
213 lines
8.4 KiB
213 lines
8.4 KiB
// |
|
// Copyright 2022 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 "test/cpp/interop/istio_echo_server_lib.h" |
|
|
|
#include <thread> |
|
|
|
#include "absl/strings/str_cat.h" |
|
#include "absl/strings/str_format.h" |
|
#include "absl/strings/str_split.h" |
|
#include "absl/synchronization/blocking_counter.h" |
|
|
|
#include <grpcpp/client_context.h> |
|
#include <grpcpp/grpcpp.h> |
|
|
|
#include "src/core/lib/gprpp/host_port.h" |
|
#include "src/proto/grpc/testing/istio_echo.pb.h" |
|
|
|
using proto::EchoRequest; |
|
using proto::EchoResponse; |
|
using proto::EchoTestService; |
|
using proto::ForwardEchoRequest; |
|
using proto::ForwardEchoResponse; |
|
|
|
namespace grpc { |
|
namespace testing { |
|
namespace { |
|
|
|
const absl::string_view kRequestIdField = "x-request-id"; |
|
const absl::string_view kServiceVersionField = "ServiceVersion"; |
|
// const absl::string_view kServicePortField = "ServicePort"; |
|
const absl::string_view kStatusCodeField = "StatusCode"; |
|
// const absl::string_view kUrlField = "URL"; |
|
const absl::string_view kHostField = "Host"; |
|
const absl::string_view kHostnameField = "Hostname"; |
|
// const absl::string_view kMethodField = "Method"; |
|
const absl::string_view kRequestHeader = "RequestHeader"; |
|
// const absl::string_view kResponseHeader = "ResponseHeader"; |
|
// const absl::string_view kClusterField = "Cluster"; |
|
// const absl::string_view kIstioVersionField = "IstioVersion"; |
|
const absl::string_view kIpField = "IP"; // The Requester’s IP Address. |
|
|
|
absl::string_view StringRefToStringView(const string_ref& r) { |
|
return absl::string_view(r.data(), r.size()); |
|
} |
|
|
|
struct EchoCall { |
|
grpc::ClientContext context; |
|
proto::EchoResponse response; |
|
Status status; |
|
}; |
|
|
|
} // namespace |
|
|
|
EchoTestServiceImpl::EchoTestServiceImpl(std::string hostname, |
|
std::string service_version, |
|
std::string forwarding_address) |
|
: hostname_(std::move(hostname)), |
|
service_version_(std::move(service_version)), |
|
forwarding_address_(std::move(forwarding_address)) { |
|
forwarding_stub_ = EchoTestService::NewStub( |
|
CreateChannel(forwarding_address_, InsecureChannelCredentials())); |
|
} |
|
|
|
Status EchoTestServiceImpl::Echo(ServerContext* context, |
|
const EchoRequest* request, |
|
EchoResponse* response) { |
|
std::string s; |
|
absl::StrAppend(&s, kHostField, "=", |
|
StringRefToStringView(context->ExperimentalGetAuthority()), |
|
"\n"); |
|
const std::multimap<string_ref, string_ref> metadata = |
|
context->client_metadata(); |
|
for (const auto& kv : metadata) { |
|
// Skip all binary headers. |
|
if (kv.first.ends_with("-bin")) { |
|
continue; |
|
} |
|
absl::StrAppend(&s, kRequestHeader, "=", StringRefToStringView(kv.first), |
|
":", StringRefToStringView(kv.second), "\n"); |
|
} |
|
absl::string_view host; |
|
absl::string_view port; |
|
std::string peer = context->peer(); |
|
grpc_core::SplitHostPort(peer, &host, &port); |
|
// This is not a complete list, but also not all fields are used. May |
|
// need to add/remove fields later, if required by tests. Only keep the |
|
// fields needed for now. |
|
// |
|
// absl::StrAppend(&s,kServicePortField,"=",this->port_,"\n"); |
|
// absl::StrAppend(&s,kClusterField,"=",this->cluster_,"\n"); |
|
// absl::StrAppend(&s,kIstioVersionField,"=",this->istio_version_,"\n"); |
|
absl::StrAppend(&s, kServiceVersionField, "=", this->service_version_, "\n"); |
|
absl::StrAppend(&s, kIpField, "=", host, "\n"); |
|
absl::StrAppend(&s, kStatusCodeField, "=", std::to_string(200), "\n"); |
|
absl::StrAppend(&s, kHostnameField, "=", this->hostname_, "\n"); |
|
absl::StrAppend(&s, "Echo=", request->message(), "\n"); |
|
response->set_message(s); |
|
gpr_log(GPR_INFO, "Echo response:\n%s", s.c_str()); |
|
return Status::OK; |
|
} |
|
|
|
Status EchoTestServiceImpl::ForwardEcho(ServerContext* context, |
|
const ForwardEchoRequest* request, |
|
ForwardEchoResponse* response) { |
|
std::string raw_url = request->url(); |
|
size_t colon = raw_url.find_first_of(':'); |
|
std::string scheme; |
|
if (colon == std::string::npos) { |
|
return Status( |
|
StatusCode::INVALID_ARGUMENT, |
|
absl::StrFormat("No protocol configured for url %s", raw_url)); |
|
} |
|
scheme = raw_url.substr(0, colon); |
|
std::shared_ptr<Channel> channel; |
|
if (scheme == "xds") { |
|
// We can optionally add support for TLS creds, but we are primarily |
|
// concerned with proxyless-grpc here. |
|
gpr_log(GPR_INFO, "Creating channel to %s using xDS Creds", |
|
raw_url.c_str()); |
|
channel = |
|
CreateChannel(raw_url, XdsCredentials(InsecureChannelCredentials())); |
|
} else if (scheme == "grpc") { |
|
// We don't really want to test this but the istio test infrastructure needs |
|
// this to be supported. If we ever decide to add support for this properly, |
|
// we would need to add support for TLS creds here. |
|
absl::string_view address = absl::StripPrefix(raw_url, "grpc://"); |
|
gpr_log(GPR_INFO, "Creating channel to %s", std::string(address).c_str()); |
|
channel = CreateChannel(std::string(address), InsecureChannelCredentials()); |
|
} else { |
|
gpr_log(GPR_INFO, "Protocol %s not supported. Forwarding to %s", |
|
scheme.c_str(), forwarding_address_.c_str()); |
|
ClientContext forwarding_ctx; |
|
forwarding_ctx.set_deadline(context->deadline()); |
|
return forwarding_stub_->ForwardEcho(&forwarding_ctx, *request, response); |
|
} |
|
auto stub = EchoTestService::NewStub(channel); |
|
auto count = request->count() == 0 ? 1 : request->count(); |
|
// Calculate the amount of time to sleep after each call. |
|
std::chrono::duration<double> duration_per_query = |
|
std::chrono::nanoseconds::zero(); |
|
if (request->qps() > 0) { |
|
duration_per_query = |
|
std::chrono::nanoseconds(std::chrono::seconds(1)) / request->qps(); |
|
} |
|
std::vector<EchoCall> calls(count); |
|
EchoRequest echo_request; |
|
echo_request.set_message(request->message()); |
|
absl::BlockingCounter counter(count); |
|
for (int i = 0; i < count; ++i) { |
|
calls[i].context.AddMetadata(std::string(kRequestIdField), |
|
std::to_string(i)); |
|
for (const auto& header : request->headers()) { |
|
if (header.key() != kHostField) { |
|
calls[i].context.AddMetadata(header.key(), header.value()); |
|
} |
|
} |
|
constexpr int kDefaultTimeout = 5 * 1000 * 1000 /* 5 seconds */; |
|
std::chrono::system_clock::time_point deadline = |
|
std::chrono::system_clock::now() + |
|
std::chrono::microseconds(request->timeout_micros() > 0 |
|
? request->timeout_micros() |
|
: kDefaultTimeout); |
|
calls[i].context.set_deadline(deadline); |
|
stub->async()->Echo(&calls[i].context, &echo_request, &calls[i].response, |
|
[&, index = i](Status s) { |
|
calls[index].status = s; |
|
counter.DecrementCount(); |
|
}); |
|
std::this_thread::sleep_for(duration_per_query); |
|
} |
|
// Wait for all calls to be done. |
|
counter.Wait(); |
|
for (int i = 0; i < count; ++i) { |
|
if (calls[i].status.ok()) { |
|
std::string body; |
|
// The test infrastructure might expect the entire struct instead of |
|
// just the message. |
|
absl::StrAppend(&body, absl::StrFormat("[%d] grpcecho.Echo(%s)\n", i, |
|
request->message())); |
|
auto contents = |
|
absl::StrSplit(calls[i].response.message(), '\n', absl::SkipEmpty()); |
|
for (const auto& line : contents) { |
|
absl::StrAppend(&body, absl::StrFormat("[%d body] %s\n", i, line)); |
|
} |
|
response->add_output(body); |
|
gpr_log(GPR_INFO, "Forward Echo response:%d\n%s", i, body.c_str()); |
|
} else { |
|
gpr_log(GPR_ERROR, "RPC %d failed %d: %s", i, |
|
calls[i].status.error_code(), |
|
calls[i].status.error_message().c_str()); |
|
response->clear_output(); |
|
return calls[i].status; |
|
} |
|
} |
|
return Status::OK; |
|
} |
|
|
|
} // namespace testing |
|
} // namespace grpc
|
|
|