diff options
author | hcaseyal <hcaseyal@gmail.com> | 2018-12-14 15:11:36 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-14 15:11:36 -0800 |
commit | 39bd784e3679b9318504160a0c7419a5f5ca1747 (patch) | |
tree | 596c01e186c78613445b0a14fa85754fd02d8b63 /examples | |
parent | c665fcfd3f9f2e1b4da1ee57f3c3063a0c80ba59 (diff) | |
parent | 580d43d03f20b0b92f4b7ae950225a1dcd3387a0 (diff) |
Merge pull request #17504 from hcaseyal/metadata_example
Metadata tutorial
Diffstat (limited to 'examples')
-rw-r--r-- | examples/BUILD | 15 | ||||
-rw-r--r-- | examples/cpp/metadata/Makefile | 96 | ||||
-rw-r--r-- | examples/cpp/metadata/README.md | 66 | ||||
-rw-r--r-- | examples/cpp/metadata/greeter_client.cc | 95 | ||||
-rw-r--r-- | examples/cpp/metadata/greeter_server.cc | 94 |
5 files changed, 366 insertions, 0 deletions
diff --git a/examples/BUILD b/examples/BUILD index 0f18cfa9ba..22f2f0a4f1 100644 --- a/examples/BUILD +++ b/examples/BUILD @@ -51,3 +51,18 @@ cc_binary( defines = ["BAZEL_BUILD"], deps = [":helloworld", "//:grpc++"], ) + +cc_binary( + name = "metadata_client", + srcs = ["cpp/metadata/greeter_client.cc"], + defines = ["BAZEL_BUILD"], + deps = [":helloworld", "//:grpc++"], +) + +cc_binary( + name = "metadata_server", + srcs = ["cpp/metadata/greeter_server.cc"], + defines = ["BAZEL_BUILD"], + deps = [":helloworld", "//:grpc++"], +) + diff --git a/examples/cpp/metadata/Makefile b/examples/cpp/metadata/Makefile new file mode 100644 index 0000000000..46be8bfaa3 --- /dev/null +++ b/examples/cpp/metadata/Makefile @@ -0,0 +1,96 @@ +# +# 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. +# + HOST_SYSTEM = $(shell uname | cut -f 1 -d_) +SYSTEM ?= $(HOST_SYSTEM) +CXX = g++ +CPPFLAGS += `pkg-config --cflags protobuf grpc` +CXXFLAGS += -std=c++11 +ifeq ($(SYSTEM),Darwin) +LDFLAGS += -L/usr/local/lib `pkg-config --libs protobuf grpc++ grpc`\ + -lgrpc++_reflection\ + -ldl +else +LDFLAGS += -L/usr/local/lib `pkg-config --libs protobuf grpc++ grpc`\ + -Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed\ + -ldl +endif +PROTOC = protoc +GRPC_CPP_PLUGIN = grpc_cpp_plugin +GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)` + PROTOS_PATH = ../../protos + vpath %.proto $(PROTOS_PATH) + all: system-check greeter_client greeter_server + greeter_client: helloworld.pb.o helloworld.grpc.pb.o greeter_client.o + $(CXX) $^ $(LDFLAGS) -o $@ + greeter_server: helloworld.pb.o helloworld.grpc.pb.o greeter_server.o + $(CXX) $^ $(LDFLAGS) -o $@ + .PRECIOUS: %.grpc.pb.cc +%.grpc.pb.cc: %.proto + $(PROTOC) -I $(PROTOS_PATH) --grpc_out=. --plugin=protoc-gen-grpc=$(GRPC_CPP_PLUGIN_PATH) $< + .PRECIOUS: %.pb.cc +%.pb.cc: %.proto + $(PROTOC) -I $(PROTOS_PATH) --cpp_out=. $< + clean: + rm -f *.o *.pb.cc *.pb.h greeter_client greeter_server + # The following is to test your system and ensure a smoother experience. +# They are by no means necessary to actually compile a grpc-enabled software. + PROTOC_CMD = which $(PROTOC) +PROTOC_CHECK_CMD = $(PROTOC) --version | grep -q libprotoc.3 +PLUGIN_CHECK_CMD = which $(GRPC_CPP_PLUGIN) +HAS_PROTOC = $(shell $(PROTOC_CMD) > /dev/null && echo true || echo false) +ifeq ($(HAS_PROTOC),true) +HAS_VALID_PROTOC = $(shell $(PROTOC_CHECK_CMD) 2> /dev/null && echo true || echo false) +endif +HAS_PLUGIN = $(shell $(PLUGIN_CHECK_CMD) > /dev/null && echo true || echo false) + SYSTEM_OK = false +ifeq ($(HAS_VALID_PROTOC),true) +ifeq ($(HAS_PLUGIN),true) +SYSTEM_OK = true +endif +endif + system-check: +ifneq ($(HAS_VALID_PROTOC),true) + @echo " DEPENDENCY ERROR" + @echo + @echo "You don't have protoc 3.0.0 installed in your path." + @echo "Please install Google protocol buffers 3.0.0 and its compiler." + @echo "You can find it here:" + @echo + @echo " https://github.com/google/protobuf/releases/tag/v3.0.0" + @echo + @echo "Here is what I get when trying to evaluate your version of protoc:" + @echo + -$(PROTOC) --version + @echo + @echo +endif +ifneq ($(HAS_PLUGIN),true) + @echo " DEPENDENCY ERROR" + @echo + @echo "You don't have the grpc c++ protobuf plugin installed in your path." + @echo "Please install grpc. You can find it here:" + @echo + @echo " https://github.com/grpc/grpc" + @echo + @echo "Here is what I get when trying to detect if you have the plugin:" + @echo + -which $(GRPC_CPP_PLUGIN) + @echo + @echo +endif +ifneq ($(SYSTEM_OK),true) + @false +endif diff --git a/examples/cpp/metadata/README.md b/examples/cpp/metadata/README.md new file mode 100644 index 0000000000..96ad3d19bd --- /dev/null +++ b/examples/cpp/metadata/README.md @@ -0,0 +1,66 @@ +# Metadata Example + +## Overview + +This example shows you how to add custom headers on the client and server and +how to access them. + +Custom metadata must follow the "Custom-Metadata" format listed in +https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md, with the +exception of binary headers, which don't have to be base64 encoded. + +### Get the tutorial source code + The example code for this and our other examples lives in the `examples` directory. Clone this repository to your local machine by running the following command: + ```sh +$ git clone -b $(curl -L https://grpc.io/release) https://github.com/grpc/grpc +``` + Change your current directory to examples/cpp/metadata + ```sh +$ cd examples/cpp/metadata +``` + +### Generating gRPC code + To generate the client and server side interfaces: + ```sh +$ make helloworld.grpc.pb.cc helloworld.pb.cc +``` +Which internally invokes the proto-compiler as: + ```sh +$ protoc -I ../../protos/ --grpc_out=. --plugin=protoc-gen-grpc=grpc_cpp_plugin ../../protos/helloworld.proto +$ protoc -I ../../protos/ --cpp_out=. ../../protos/helloworld.proto +``` +### Try it! +Build client and server: + +```sh +$ make +``` + +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 in the client terminal: + +"Client received initial metadata from server: initial metadata value" +"Client received trailing metadata from server: trailing metadata value" +"Client received message: Hello World" + + +And in the server terminal: + +"Header key: custom-bin , value: 01234567" +"Header key: custom-header , value: Custom Value" +"Header key: user-agent , value: grpc-c++/1.16.0-dev grpc-c/6.0.0-dev (linux; chttp2; gao)" + +We did not add the user-agent metadata as a custom header. This shows how +the gRPC framework adds some headers under the hood that may show up in the +metadata map. diff --git a/examples/cpp/metadata/greeter_client.cc b/examples/cpp/metadata/greeter_client.cc new file mode 100644 index 0000000000..8049438993 --- /dev/null +++ b/examples/cpp/metadata/greeter_client.cc @@ -0,0 +1,95 @@ +/* + * + * 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 <iostream> +#include <memory> +#include <string> + +#include <grpcpp/grpcpp.h> + +#ifdef BAZEL_BUILD +#include "examples/protos/helloworld.grpc.pb.h" +#else +#include "helloworld.grpc.pb.h" +#endif + +using grpc::Channel; +using grpc::ClientContext; +using grpc::Status; +using helloworld::HelloRequest; +using helloworld::HelloReply; +using helloworld::Greeter; + +class CustomHeaderClient { + public: + CustomHeaderClient(std::shared_ptr<Channel> channel) + : stub_(Greeter::NewStub(channel)) {} + + // Assembles the client's payload, sends it and presents the response back + // from the server. + std::string 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; + + // Setting custom metadata to be sent to the server + context.AddMetadata("custom-header", "Custom Value"); + + // Setting custom binary metadata + char bytes[8] = {'\0', '\1', '\2', '\3', + '\4', '\5', '\6', '\7'}; + context.AddMetadata("custom-bin", grpc::string(bytes, 8)); + + // The actual RPC. + Status status = stub_->SayHello(&context, request, &reply); + + // Act upon its status. + if (status.ok()) { + std::cout << "Client received initial metadata from server: " << context.GetServerInitialMetadata().find("custom-server-metadata")->second << std::endl; + std::cout << "Client received trailing metadata from server: " << context.GetServerTrailingMetadata().find("custom-trailing-metadata")->second << std::endl; + return reply.message(); + } else { + std::cout << status.error_code() << ": " << status.error_message() + << std::endl; + return "RPC failed"; + } + } + + private: + std::unique_ptr<Greeter::Stub> stub_; +}; + +int main(int argc, char** argv) { + // Instantiate the client. It requires a channel, out of which the actual RPCs + // are created. This channel models a connection to an endpoint (in this case, + // localhost at port 50051). We indicate that the channel isn't authenticated + // (use of InsecureChannelCredentials()). + CustomHeaderClient greeter(grpc::CreateChannel( + "localhost:50051", grpc::InsecureChannelCredentials())); + std::string user("world"); + std::string reply = greeter.SayHello(user); + std::cout << "Client received message: " << reply << std::endl; + return 0; +} diff --git a/examples/cpp/metadata/greeter_server.cc b/examples/cpp/metadata/greeter_server.cc new file mode 100644 index 0000000000..a9a4f33cb0 --- /dev/null +++ b/examples/cpp/metadata/greeter_server.cc @@ -0,0 +1,94 @@ +/* + * + * 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 <iostream> +#include <memory> +#include <string> + +#include <grpcpp/grpcpp.h> + +#ifdef BAZEL_BUILD +#include "examples/protos/helloworld.grpc.pb.h" +#else +#include "helloworld.grpc.pb.h" +#endif + +using grpc::Server; +using grpc::ServerBuilder; +using grpc::ServerContext; +using grpc::Status; +using helloworld::HelloRequest; +using helloworld::HelloReply; +using helloworld::Greeter; + +// Logic and data behind the server's behavior. +class GreeterServiceImpl final : public Greeter::Service { + Status SayHello(ServerContext* context, const HelloRequest* request, + HelloReply* reply) override { + std::string prefix("Hello "); + + // Get the client's initial metadata + std::cout << "Client metadata: " << std::endl; + const std::multimap<grpc::string_ref, grpc::string_ref> metadata = context->client_metadata(); + for (auto iter = metadata.begin(); iter != metadata.end(); ++iter) { + std::cout << "Header key: " << iter->first << ", value: "; + // Check for binary value + size_t isbin = iter->first.find("-bin"); + if ((isbin != std::string::npos) && (isbin + 4 == iter->first.size())) { + std::cout << std::hex; + for (auto c : iter->second) { + std::cout << static_cast<unsigned int>(c); + } + std::cout << std::dec; + } else { + std::cout << iter->second; + } + std::cout << std::endl; + } + + context->AddInitialMetadata("custom-server-metadata", "initial metadata value"); + context->AddTrailingMetadata("custom-trailing-metadata", "trailing metadata value"); + reply->set_message(prefix + request->name()); + return Status::OK; + } +}; + +void RunServer() { + std::string server_address("0.0.0.0:50051"); + GreeterServiceImpl service; + + 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) { + RunServer(); + + return 0; +} |