[Example] Adding error_handling & error_details example (#33129)

Adding a new example showing how to propagate error status from the
server to the client with optional detail information.
pull/33206/head
Esun Kim 2 years ago committed by GitHub
parent 988c85535d
commit f3574e3f64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 45
      examples/cpp/error_details/BUILD
  2. 113
      examples/cpp/error_details/CMakeLists.txt
  3. 33
      examples/cpp/error_details/README.md
  4. 139
      examples/cpp/error_details/greeter_client.cc
  5. 117
      examples/cpp/error_details/greeter_server.cc
  6. 41
      examples/cpp/error_handling/BUILD
  7. 70
      examples/cpp/error_handling/CMakeLists.txt
  8. 33
      examples/cpp/error_handling/README.md
  9. 107
      examples/cpp/error_handling/greeter_client.cc
  10. 97
      examples/cpp/error_handling/greeter_server.cc

@ -0,0 +1,45 @@
# Copyright 2023 the 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.
licenses(["notice"])
cc_binary(
name = "greeter_client",
srcs = ["greeter_client.cc"],
defines = ["BAZEL_BUILD"],
deps = [
"//:grpc++",
"//examples/protos:helloworld_cc_grpc",
"//src/proto/grpc/status:status_proto",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/flags:parse",
"@com_google_googleapis//google/rpc:error_details_cc_proto",
],
)
cc_binary(
name = "greeter_server",
srcs = ["greeter_server.cc"],
defines = ["BAZEL_BUILD"],
deps = [
"//:grpc++",
"//:grpc++_reflection",
"//examples/protos:helloworld_cc_grpc",
"//src/proto/grpc/status:status_proto",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/flags:parse",
"@com_google_absl//absl/strings:str_format",
"@com_google_googleapis//google/rpc:error_details_cc_proto",
],
)

@ -0,0 +1,113 @@
# Copyright 2023 the 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.
#
# cmake build file for C++ helloworld example.
# Assumes protobuf and gRPC have been installed using cmake.
# See cmake_externalproject/CMakeLists.txt for all-in-one cmake build
# that automatically builds all the dependencies before building helloworld.
cmake_minimum_required(VERSION 3.8)
project(ErrorDetails C CXX)
include(../cmake/common.cmake)
# Proto files
get_filename_component(hw_proto "../../protos/helloworld.proto" ABSOLUTE)
get_filename_component(hw_proto_path "${hw_proto}" PATH)
get_filename_component(status_proto "../../../third_party/googleapis/google/rpc/status.proto" ABSOLUTE)
get_filename_component(status_proto_path "${status_proto}" PATH)
get_filename_component(error_details_proto "../../../third_party/googleapis/google/rpc/error_details.proto" ABSOLUTE)
get_filename_component(error_details_proto_path "${error_details_proto}" PATH)
get_filename_component(protobuf_proto_path "../../../third_party/protobuf/src" ABSOLUTE)
# Generated sources
set(hw_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.pb.cc")
set(hw_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.pb.h")
set(hw_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.grpc.pb.cc")
set(hw_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.grpc.pb.h")
add_custom_command(
OUTPUT "${hw_proto_srcs}" "${hw_proto_hdrs}" "${hw_grpc_srcs}" "${hw_grpc_hdrs}"
COMMAND ${_PROTOBUF_PROTOC}
ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}"
--cpp_out "${CMAKE_CURRENT_BINARY_DIR}"
-I "${hw_proto_path}"
--plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}"
"${hw_proto}"
DEPENDS "${hw_proto}")
set(status_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/status.pb.cc")
set(status_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/status.pb.h")
add_custom_command(
OUTPUT "${status_proto_srcs}" "${status_proto_hdrs}"
COMMAND ${_PROTOBUF_PROTOC}
ARGS
--cpp_out "${CMAKE_CURRENT_BINARY_DIR}"
-I "${status_proto_path}"
-I "${protobuf_proto_path}"
"${status_proto}"
DEPENDS "${status_proto}")
set(error_details_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/error_details.pb.cc")
set(error_details_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/error_details.pb.h")
add_custom_command(
OUTPUT "${error_details_proto_srcs}" "${error_details_proto_hdrs}"
COMMAND ${_PROTOBUF_PROTOC}
ARGS
--cpp_out "${CMAKE_CURRENT_BINARY_DIR}"
-I "${error_details_proto_path}"
-I "${protobuf_proto_path}"
"${error_details_proto}"
DEPENDS "${error_details_proto}")
# Include generated *.pb.h files
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
# hw_grpc_proto
add_library(hw_grpc_proto
${hw_grpc_srcs}
${hw_grpc_hdrs}
${hw_proto_srcs}
${hw_proto_hdrs})
target_link_libraries(hw_grpc_proto
${_REFLECTION}
${_GRPC_GRPCPP}
${_PROTOBUF_LIBPROTOBUF})
# status_proto
add_library(status_proto
${status_proto_srcs}
${status_proto_hdrs})
target_link_libraries(status_proto
${_PROTOBUF_LIBPROTOBUF})
# error_details_proto
add_library(error_details_proto
${error_details_proto_srcs}
${error_details_proto_hdrs})
target_link_libraries(error_details_proto
${_PROTOBUF_LIBPROTOBUF})
# Targets greeter_(client|server)
foreach(_target
greeter_client greeter_server)
add_executable(${_target} "${_target}.cc")
target_link_libraries(${_target}
hw_grpc_proto
status_proto
error_details_proto
absl::flags
absl::flags_parse
${_REFLECTION}
${_GRPC_GRPCPP}
${_PROTOBUF_LIBPROTOBUF})
endforeach()

@ -0,0 +1,33 @@
# Error Details Example
## Overview
This example shows you how to return error with details from the server
and how to handle it on the client.
### Try it!
Once you have working gRPC, you can build this example using either bazel or cmake.
Run the server, which will listen on port 50051:
```sh
$ ./greeter_server
```
Run the client (in a different terminal):
```sh
$ ./greeter_client
```
If things go smoothly, you will see the client output:
```
### Send: SayHello(name=World)
Ok. ReplyMessage=Hello World
### Send: SayHello(name=World)
Failed. Code=8 Message=Request limit exceeded
Details:
- Quota: subject=name: World description=Limit one greeting per person
```

@ -0,0 +1,139 @@
// 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 <condition_variable>
#include <iostream>
#include <memory>
#include <mutex>
#include <string>
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "absl/strings/str_format.h"
#include <grpcpp/grpcpp.h>
#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#include "google/rpc/error_details.pb.h"
#include "src/proto/grpc/status/status.pb.h"
#else
#include "error_details.pb.h"
#include "helloworld.grpc.pb.h"
#include "status.pb.h"
#endif
ABSL_FLAG(std::string, target, "localhost:50051", "Server address");
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
class GreeterClient {
public:
GreeterClient(std::shared_ptr<Channel> channel)
: stub_(Greeter::NewStub(channel)) {}
// Assembles the client's payload, sends it and prints the response back
// from the server.
void SayHello(const std::string& user) {
// Data we are sending to the server.
HelloRequest request;
request.set_name(user);
// Container for the data we expect from the server.
HelloReply reply;
// Context for the client. It could be used to convey extra information to
// the server and/or tweak certain RPC behaviors.
ClientContext context;
// The actual RPC.
std::mutex mu;
std::condition_variable cv;
bool done = false;
Status status;
std::cout << absl::StrFormat("### Send: SayHello(name=%s)", user)
<< std::endl;
stub_->async()->SayHello(&context, &request, &reply, [&](Status s) {
status = std::move(s);
std::lock_guard<std::mutex> lock(mu);
done = true;
cv.notify_one();
});
std::unique_lock<std::mutex> lock(mu);
while (!done) {
cv.wait(lock);
}
// Handles the reply
if (status.ok()) {
std::cout << absl::StrFormat("Ok. ReplyMessage=%s", reply.message())
<< std::endl;
} else {
std::cout << absl::StrFormat("Failed. Code=%d Message=%s",
status.error_code(), status.error_message())
<< std::endl;
PrintErrorDetails(status);
}
}
void PrintErrorDetails(grpc::Status status) {
auto error_details = status.error_details();
if (error_details.empty()) {
return;
}
// If error_details are present in the status, this tries to deserialize
// those assuming they're proto messages.
google::rpc::Status s;
if (!s.ParseFromString(error_details)) {
std::cout << "Failed to deserialize `error_details`" << std::endl;
return;
}
std::cout << absl::StrFormat("Details:") << std::endl;
for (auto& detail : s.details()) {
google::rpc::QuotaFailure quota_failure;
if (detail.UnpackTo(&quota_failure)) {
for (auto& violation : quota_failure.violations()) {
std::cout << absl::StrFormat("- Quota: subject=%s description=%s",
violation.subject(),
violation.description())
<< std::endl;
}
} else {
std::cout << "Unknown error_detail: " + detail.type_url() << std::endl;
}
}
}
private:
std::unique_ptr<Greeter::Stub> stub_;
};
int main(int argc, char** argv) {
absl::ParseCommandLine(argc, argv);
// Instantiate the client. It requires a channel, out of which the actual RPCs
// are created. This channel models a connection to an endpoint specified by
// the argument "--target=" which is the only expected argument.
std::string target_str = absl::GetFlag(FLAGS_target);
// We indicate that the channel isn't authenticated (use of
// InsecureChannelCredentials()).
GreeterClient greeter(
grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials()));
// Sends a first new name, expecting OK
greeter.SayHello("World");
// Sends a duplicate name, expecting RESOURCE_EXHAUSTED with error_details
greeter.SayHello("World");
return 0;
}

@ -0,0 +1,117 @@
// 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 <iostream>
#include <memory>
#include <string>
#include <unordered_set>
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "absl/strings/str_format.h"
#include "absl/synchronization/mutex.h"
#include <grpcpp/ext/proto_server_reflection_plugin.h>
#include <grpcpp/grpcpp.h>
#include <grpcpp/health_check_service_interface.h>
#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#include "google/rpc/error_details.pb.h"
#include "src/proto/grpc/status/status.pb.h"
#else
#include "error_details.pb.h"
#include "helloworld.grpc.pb.h"
#include "status.pb.h"
#endif
ABSL_FLAG(uint16_t, port, 50051, "Server port for the service");
using grpc::CallbackServerContext;
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerUnaryReactor;
using grpc::Status;
using grpc::StatusCode;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
// Logic and data behind the server's behavior.
class GreeterServiceImpl final : public Greeter::CallbackService {
ServerUnaryReactor* SayHello(CallbackServerContext* context,
const HelloRequest* request,
HelloReply* reply) override {
ServerUnaryReactor* reactor = context->DefaultReactor();
Status status;
// Checks whether it is a duplicate request
if (CheckRequestDuplicate(request->name())) {
// Returns an error status with more detailed information.
// In this example, the status has additional google::rpc::QuotaFailure
// conveying additional information about the error.
google::rpc::QuotaFailure quota_failure;
auto violation = quota_failure.add_violations();
violation->set_subject("name: " + request->name());
violation->set_description("Limit one greeting per person");
google::rpc::Status s;
s.set_code(static_cast<int>(StatusCode::RESOURCE_EXHAUSTED));
s.set_message("Request limit exceeded");
s.add_details()->PackFrom(quota_failure);
status = Status(StatusCode::RESOURCE_EXHAUSTED, "Request limit exceeded",
s.SerializeAsString());
} else {
reply->set_message(absl::StrFormat("Hello %s", request->name()));
status = Status::OK;
}
reactor->Finish(status);
return reactor;
}
private:
bool CheckRequestDuplicate(const std::string& name) {
absl::MutexLock lock(&mu_);
return !request_name_set_.insert(name).second;
}
absl::Mutex mu_;
std::unordered_set<std::string> request_name_set_;
};
void RunServer(uint16_t port) {
std::string server_address = absl::StrFormat("0.0.0.0:%d", port);
GreeterServiceImpl service;
grpc::EnableDefaultHealthCheckService(true);
grpc::reflection::InitProtoReflectionServerBuilderPlugin();
ServerBuilder builder;
// Listen on the given address without any authentication mechanism.
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
// Register "service" as the instance through which we'll communicate with
// clients. In this case it corresponds to an *synchronous* service.
builder.RegisterService(&service);
// Finally assemble the server.
std::unique_ptr<Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << server_address << std::endl;
// Wait for the server to shutdown. Note that some other thread must be
// responsible for shutting down the server for this call to ever return.
server->Wait();
}
int main(int argc, char** argv) {
absl::ParseCommandLine(argc, argv);
RunServer(absl::GetFlag(FLAGS_port));
return 0;
}

@ -0,0 +1,41 @@
# Copyright 2023 the 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.
licenses(["notice"])
cc_binary(
name = "greeter_client",
srcs = ["greeter_client.cc"],
defines = ["BAZEL_BUILD"],
deps = [
"//:grpc++",
"//examples/protos:helloworld_cc_grpc",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/flags:parse",
],
)
cc_binary(
name = "greeter_server",
srcs = ["greeter_server.cc"],
defines = ["BAZEL_BUILD"],
deps = [
"//:grpc++",
"//:grpc++_reflection",
"//examples/protos:helloworld_cc_grpc",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/flags:parse",
"@com_google_absl//absl/strings:str_format",
],
)

@ -0,0 +1,70 @@
# Copyright 2023 the 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.
#
# cmake build file for C++ helloworld example.
# Assumes protobuf and gRPC have been installed using cmake.
# See cmake_externalproject/CMakeLists.txt for all-in-one cmake build
# that automatically builds all the dependencies before building helloworld.
cmake_minimum_required(VERSION 3.8)
project(ErrorHandling C CXX)
include(../cmake/common.cmake)
# Proto files
get_filename_component(hw_proto "../../protos/helloworld.proto" ABSOLUTE)
get_filename_component(hw_proto_path "${hw_proto}" PATH)
# Generated sources
set(hw_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.pb.cc")
set(hw_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.pb.h")
set(hw_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.grpc.pb.cc")
set(hw_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.grpc.pb.h")
add_custom_command(
OUTPUT "${hw_proto_srcs}" "${hw_proto_hdrs}" "${hw_grpc_srcs}" "${hw_grpc_hdrs}"
COMMAND ${_PROTOBUF_PROTOC}
ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}"
--cpp_out "${CMAKE_CURRENT_BINARY_DIR}"
-I "${hw_proto_path}"
--plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}"
"${hw_proto}"
DEPENDS "${hw_proto}")
# Include generated *.pb.h files
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
# hw_grpc_proto
add_library(hw_grpc_proto
${hw_grpc_srcs}
${hw_grpc_hdrs}
${hw_proto_srcs}
${hw_proto_hdrs})
target_link_libraries(hw_grpc_proto
${_REFLECTION}
${_GRPC_GRPCPP}
${_PROTOBUF_LIBPROTOBUF})
# Targets greeter_(client|server)
foreach(_target
greeter_client greeter_server)
add_executable(${_target} "${_target}.cc")
target_link_libraries(${_target}
hw_grpc_proto
absl::flags
absl::flags_parse
${_REFLECTION}
${_GRPC_GRPCPP}
${_PROTOBUF_LIBPROTOBUF})
endforeach()

@ -0,0 +1,33 @@
# Error Handling Example
## Overview
This example shows you how to return error from the server and
how to handle it on the client.
### Try it!
Once you have working gRPC, you can build this example using either bazel or cmake.
Run the server, which will listen on port 50051:
```sh
$ ./greeter_server
```
Run the client (in a different terminal):
```sh
$ ./greeter_client
```
If things go smoothly, you will see the client output:
```
### Send: SayHello(name=)
Failed. Code=3 Message=Length of `Name` should be between 1 and 10
### Send: SayHello(name=ItsTooLongName)
Failed. Code=3 Message=Length of `Name` should be between 1 and 10
### Send: SayHello(name=World)
Ok. ReplyMessage=Hello World
```

@ -0,0 +1,107 @@
// 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 <condition_variable>
#include <iostream>
#include <memory>
#include <mutex>
#include <string>
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "absl/strings/str_format.h"
#include <grpcpp/grpcpp.h>
#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif
ABSL_FLAG(std::string, target, "localhost:50051", "Server address");
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
class GreeterClient {
public:
GreeterClient(std::shared_ptr<Channel> channel)
: stub_(Greeter::NewStub(channel)) {}
// Assembles the client's payload, sends it and prints the response back
// from the server.
void SayHello(const std::string& user) {
// Data we are sending to the server.
HelloRequest request;
request.set_name(user);
// Container for the data we expect from the server.
HelloReply reply;
// Context for the client. It could be used to convey extra information to
// the server and/or tweak certain RPC behaviors.
ClientContext context;
// The actual RPC.
std::mutex mu;
std::condition_variable cv;
bool done = false;
Status status;
std::cout << absl::StrFormat("### Send: SayHello(name=%s)", user)
<< std::endl;
stub_->async()->SayHello(&context, &request, &reply, [&](Status s) {
status = std::move(s);
std::lock_guard<std::mutex> lock(mu);
done = true;
cv.notify_one();
});
std::unique_lock<std::mutex> lock(mu);
while (!done) {
cv.wait(lock);
}
// Handles the reply
if (status.ok()) {
std::cout << absl::StrFormat("Ok. ReplyMessage=%s", reply.message())
<< std::endl;
} else {
std::cout << absl::StrFormat("Failed. Code=%d Message=%s",
status.error_code(), status.error_message())
<< std::endl;
}
}
private:
std::unique_ptr<Greeter::Stub> stub_;
};
int main(int argc, char** argv) {
absl::ParseCommandLine(argc, argv);
// Instantiate the client. It requires a channel, out of which the actual RPCs
// are created. This channel models a connection to an endpoint specified by
// the argument "--target=" which is the only expected argument.
std::string target_str = absl::GetFlag(FLAGS_target);
// We indicate that the channel isn't authenticated (use of
// InsecureChannelCredentials()).
GreeterClient greeter(
grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials()));
// Sends an empty name, expecting INVALID_ARGUMENT
greeter.SayHello("");
// Sends a too long name, expecting INVALID_ARGUMENT
greeter.SayHello("ItsTooLongName");
// Sends a first new name, expecting OK
greeter.SayHello("World");
return 0;
}

@ -0,0 +1,97 @@
// 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 <iostream>
#include <memory>
#include <string>
#include <unordered_set>
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "absl/strings/str_format.h"
#include "absl/synchronization/mutex.h"
#include <grpcpp/ext/proto_server_reflection_plugin.h>
#include <grpcpp/grpcpp.h>
#include <grpcpp/health_check_service_interface.h>
#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif
ABSL_FLAG(uint16_t, port, 50051, "Server port for the service");
using grpc::CallbackServerContext;
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerUnaryReactor;
using grpc::Status;
using grpc::StatusCode;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
// Logic and data behind the server's behavior.
class GreeterServiceImpl final : public Greeter::CallbackService {
ServerUnaryReactor* SayHello(CallbackServerContext* context,
const HelloRequest* request,
HelloReply* reply) override {
ServerUnaryReactor* reactor = context->DefaultReactor();
Status status;
// Checks the length of name
if (request->name().empty() || request->name().length() > 10) {
// Returns an error status with error code and message.
status = Status(StatusCode::INVALID_ARGUMENT,
"Length of `Name` should be between 1 and 10");
} else {
reply->set_message(absl::StrFormat("Hello %s", request->name()));
status = Status::OK;
}
reactor->Finish(status);
return reactor;
}
private:
absl::Mutex mu_;
std::unordered_set<std::string> request_name_set_;
};
void RunServer(uint16_t port) {
std::string server_address = absl::StrFormat("0.0.0.0:%d", port);
GreeterServiceImpl service;
grpc::EnableDefaultHealthCheckService(true);
grpc::reflection::InitProtoReflectionServerBuilderPlugin();
ServerBuilder builder;
// Listen on the given address without any authentication mechanism.
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
// Register "service" as the instance through which we'll communicate with
// clients. In this case it corresponds to an *synchronous* service.
builder.RegisterService(&service);
// Finally assemble the server.
std::unique_ptr<Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << server_address << std::endl;
// Wait for the server to shutdown. Note that some other thread must be
// responsible for shutting down the server for this call to ever return.
server->Wait();
}
int main(int argc, char** argv) {
absl::ParseCommandLine(argc, argv);
RunServer(absl::GetFlag(FLAGS_port));
return 0;
}
Loading…
Cancel
Save