diff options
author | David Garcia Quintas <dgq@google.com> | 2016-01-19 14:51:55 -0800 |
---|---|---|
committer | David Garcia Quintas <dgq@google.com> | 2016-01-19 14:51:55 -0800 |
commit | 4a0f8ea5355166706cabdd1ca984ae10341ab64b (patch) | |
tree | 5b5f984c61ad1a851fefd5780eba640df6343cae | |
parent | 3bd7ecce16d8bda27ac1268641183512b54f4721 (diff) | |
parent | 1ac309345094de17cbb9ed0be4870096f451643d (diff) |
Merge branch 'sync_async_mix' of https://github.com/yang-g/grpc into yang-g-sync_async_mix
35 files changed, 1883 insertions, 642 deletions
@@ -785,6 +785,7 @@ cc_library( "include/grpc++/impl/call.h", "include/grpc++/impl/client_unary_call.h", "include/grpc++/impl/grpc_library.h", + "include/grpc++/impl/method_handler_impl.h", "include/grpc++/impl/proto_utils.h", "include/grpc++/impl/rpc_method.h", "include/grpc++/impl/rpc_service_method.h", @@ -878,6 +879,7 @@ cc_library( "include/grpc++/impl/call.h", "include/grpc++/impl/client_unary_call.h", "include/grpc++/impl/grpc_library.h", + "include/grpc++/impl/method_handler_impl.h", "include/grpc++/impl/proto_utils.h", "include/grpc++/impl/rpc_method.h", "include/grpc++/impl/rpc_service_method.h", @@ -914,6 +914,7 @@ grpc_csharp_plugin: $(BINDIR)/$(CONFIG)/grpc_csharp_plugin grpc_objective_c_plugin: $(BINDIR)/$(CONFIG)/grpc_objective_c_plugin grpc_python_plugin: $(BINDIR)/$(CONFIG)/grpc_python_plugin grpc_ruby_plugin: $(BINDIR)/$(CONFIG)/grpc_ruby_plugin +hybrid_end2end_test: $(BINDIR)/$(CONFIG)/hybrid_end2end_test interop_client: $(BINDIR)/$(CONFIG)/interop_client interop_server: $(BINDIR)/$(CONFIG)/interop_server interop_test: $(BINDIR)/$(CONFIG)/interop_test @@ -1263,6 +1264,7 @@ buildtests_cxx: buildtests_zookeeper privatelibs_cxx \ $(BINDIR)/$(CONFIG)/generic_async_streaming_ping_pong_test \ $(BINDIR)/$(CONFIG)/generic_end2end_test \ $(BINDIR)/$(CONFIG)/grpc_cli \ + $(BINDIR)/$(CONFIG)/hybrid_end2end_test \ $(BINDIR)/$(CONFIG)/interop_client \ $(BINDIR)/$(CONFIG)/interop_server \ $(BINDIR)/$(CONFIG)/interop_test \ @@ -1564,6 +1566,8 @@ test_cxx: test_zookeeper buildtests_cxx $(Q) $(BINDIR)/$(CONFIG)/generic_async_streaming_ping_pong_test || ( echo test generic_async_streaming_ping_pong_test failed ; exit 1 ) $(E) "[RUN] Testing generic_end2end_test" $(Q) $(BINDIR)/$(CONFIG)/generic_end2end_test || ( echo test generic_end2end_test failed ; exit 1 ) + $(E) "[RUN] Testing hybrid_end2end_test" + $(Q) $(BINDIR)/$(CONFIG)/hybrid_end2end_test || ( echo test hybrid_end2end_test failed ; exit 1 ) $(E) "[RUN] Testing interop_test" $(Q) $(BINDIR)/$(CONFIG)/interop_test || ( echo test interop_test failed ; exit 1 ) $(E) "[RUN] Testing mock_test" @@ -2987,6 +2991,7 @@ PUBLIC_HEADERS_CXX += \ include/grpc++/impl/call.h \ include/grpc++/impl/client_unary_call.h \ include/grpc++/impl/grpc_library.h \ + include/grpc++/impl/method_handler_impl.h \ include/grpc++/impl/proto_utils.h \ include/grpc++/impl/rpc_method.h \ include/grpc++/impl/rpc_service_method.h \ @@ -3158,6 +3163,8 @@ LIBGRPC++_TEST_UTIL_SRC = \ $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc \ $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc \ $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc \ + test/cpp/end2end/test_service_impl.cc \ + test/cpp/util/byte_buffer_proto_helper.cc \ test/cpp/util/cli_call.cc \ test/cpp/util/create_test_channel.cc \ test/cpp/util/string_ref_helper.cc \ @@ -3206,6 +3213,8 @@ ifneq ($(NO_DEPS),true) -include $(LIBGRPC++_TEST_UTIL_OBJS:.o=.dep) endif endif +$(OBJDIR)/$(CONFIG)/test/cpp/end2end/test_service_impl.o: $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc +$(OBJDIR)/$(CONFIG)/test/cpp/util/byte_buffer_proto_helper.o: $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(OBJDIR)/$(CONFIG)/test/cpp/util/cli_call.o: $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(OBJDIR)/$(CONFIG)/test/cpp/util/create_test_channel.o: $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(OBJDIR)/$(CONFIG)/test/cpp/util/string_ref_helper.o: $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc @@ -3252,6 +3261,7 @@ PUBLIC_HEADERS_CXX += \ include/grpc++/impl/call.h \ include/grpc++/impl/client_unary_call.h \ include/grpc++/impl/grpc_library.h \ + include/grpc++/impl/method_handler_impl.h \ include/grpc++/impl/proto_utils.h \ include/grpc++/impl/rpc_method.h \ include/grpc++/impl/rpc_service_method.h \ @@ -9618,6 +9628,49 @@ ifneq ($(NO_DEPS),true) endif +HYBRID_END2END_TEST_SRC = \ + test/cpp/end2end/hybrid_end2end_test.cc \ + +HYBRID_END2END_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HYBRID_END2END_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/hybrid_end2end_test: openssl_dep_error + +else + + + + +ifeq ($(NO_PROTOBUF),true) + +# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+. + +$(BINDIR)/$(CONFIG)/hybrid_end2end_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/hybrid_end2end_test: $(PROTOBUF_DEP) $(HYBRID_END2END_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(HYBRID_END2END_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/hybrid_end2end_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/end2end/hybrid_end2end_test.o: $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + +deps_hybrid_end2end_test: $(HYBRID_END2END_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(HYBRID_END2END_TEST_OBJS:.o=.dep) +endif +endif + + ifeq ($(NO_SECURE),true) # You can't build secure targets if you don't have OpenSSL. @@ -12857,6 +12910,7 @@ test/core/end2end/tests/call_creds.c: $(OPENSSL_DEP) test/core/security/oauth2_utils.c: $(OPENSSL_DEP) test/core/util/reconnect_server.c: $(OPENSSL_DEP) test/core/util/test_tcp_server.c: $(OPENSSL_DEP) +test/cpp/end2end/test_service_impl.cc: $(OPENSSL_DEP) test/cpp/interop/client.cc: $(OPENSSL_DEP) test/cpp/interop/client_helper.cc: $(OPENSSL_DEP) test/cpp/interop/interop_client.cc: $(OPENSSL_DEP) @@ -12872,6 +12926,7 @@ test/cpp/qps/server_async.cc: $(OPENSSL_DEP) test/cpp/qps/server_sync.cc: $(OPENSSL_DEP) test/cpp/qps/timer.cc: $(OPENSSL_DEP) test/cpp/util/benchmark_config.cc: $(OPENSSL_DEP) +test/cpp/util/byte_buffer_proto_helper.cc: $(OPENSSL_DEP) test/cpp/util/cli_call.cc: $(OPENSSL_DEP) test/cpp/util/create_test_channel.cc: $(OPENSSL_DEP) test/cpp/util/string_ref_helper.cc: $(OPENSSL_DEP) diff --git a/build.yaml b/build.yaml index 70a8dee7bb..a6f7c26939 100644 --- a/build.yaml +++ b/build.yaml @@ -33,6 +33,7 @@ filegroups: - include/grpc++/impl/call.h - include/grpc++/impl/client_unary_call.h - include/grpc++/impl/grpc_library.h + - include/grpc++/impl/method_handler_impl.h - include/grpc++/impl/proto_utils.h - include/grpc++/impl/rpc_method.h - include/grpc++/impl/rpc_service_method.h @@ -636,6 +637,8 @@ libs: build: private language: c++ headers: + - test/cpp/end2end/test_service_impl.h + - test/cpp/util/byte_buffer_proto_helper.h - test/cpp/util/cli_call.h - test/cpp/util/create_test_channel.h - test/cpp/util/string_ref_helper.h @@ -644,6 +647,8 @@ libs: - src/proto/grpc/testing/echo_messages.proto - src/proto/grpc/testing/echo.proto - src/proto/grpc/testing/duplicate/echo_duplicate.proto + - test/cpp/end2end/test_service_impl.cc + - test/cpp/util/byte_buffer_proto_helper.cc - test/cpp/util/cli_call.cc - test/cpp/util/create_test_channel.cc - test/cpp/util/string_ref_helper.cc @@ -2045,6 +2050,18 @@ targets: secure: false vs_config_type: Application vs_project_guid: '{069E9D05-B78B-4751-9252-D21EBAE7DE8E}' +- name: hybrid_end2end_test + build: test + language: c++ + src: + - test/cpp/end2end/hybrid_end2end_test.cc + deps: + - grpc++_test_util + - grpc_test_util + - grpc++ + - grpc + - gpr_test_util + - gpr - name: interop_client build: test run: false diff --git a/examples/cpp/helloworld/greeter_async_server.cc b/examples/cpp/helloworld/greeter_async_server.cc index d06e7c7148..c9b1a67e95 100644 --- a/examples/cpp/helloworld/greeter_async_server.cc +++ b/examples/cpp/helloworld/greeter_async_server.cc @@ -1,6 +1,6 @@ /* * - * Copyright 2015, Google Inc. + * Copyright 2015-2016, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -67,7 +67,7 @@ class ServerImpl final { 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 *asynchronous* service. - builder.RegisterAsyncService(&service_); + builder.RegisterService(&service_); // Get hold of the completion queue used for the asynchronous communication // with the gRPC runtime. cq_ = builder.AddCompletionQueue(); diff --git a/include/grpc++/generic/async_generic_service.h b/include/grpc++/generic/async_generic_service.h index 57a2696b3b..748311dbe7 100644 --- a/include/grpc++/generic/async_generic_service.h +++ b/include/grpc++/generic/async_generic_service.h @@ -58,11 +58,7 @@ class GenericServerContext GRPC_FINAL : public ServerContext { class AsyncGenericService GRPC_FINAL { public: - // TODO(yangg) Once we can add multiple completion queues to the server - // in c core, add a CompletionQueue* argument to the ctor here. - // TODO(yangg) support methods list. AsyncGenericService() : server_(nullptr) {} - AsyncGenericService(const grpc::string& methods) : server_(nullptr) {} void RequestCall(GenericServerContext* ctx, GenericServerAsyncReaderWriter* reader_writer, diff --git a/include/grpc++/impl/method_handler_impl.h b/include/grpc++/impl/method_handler_impl.h new file mode 100644 index 0000000000..2997cb0e62 --- /dev/null +++ b/include/grpc++/impl/method_handler_impl.h @@ -0,0 +1,203 @@ +/* + * + * Copyright 2015-2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPCXX_IMPL_METHOD_HANDLER_IMPL_H +#define GRPCXX_IMPL_METHOD_HANDLER_IMPL_H + +#include <grpc++/impl/rpc_service_method.h> +#include <grpc++/support/sync_stream.h> + +namespace grpc { + +// A wrapper class of an application provided rpc method handler. +template <class ServiceType, class RequestType, class ResponseType> +class RpcMethodHandler : public MethodHandler { + public: + RpcMethodHandler( + std::function<Status(ServiceType*, ServerContext*, const RequestType*, + ResponseType*)> func, + ServiceType* service) + : func_(func), service_(service) {} + + void RunHandler(const HandlerParameter& param) GRPC_FINAL { + RequestType req; + Status status = SerializationTraits<RequestType>::Deserialize( + param.request, &req, param.max_message_size); + ResponseType rsp; + if (status.ok()) { + status = func_(service_, param.server_context, &req, &rsp); + } + + GPR_ASSERT(!param.server_context->sent_initial_metadata_); + CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, + CallOpServerSendStatus> ops; + ops.SendInitialMetadata(param.server_context->initial_metadata_); + if (status.ok()) { + status = ops.SendMessage(rsp); + } + ops.ServerSendStatus(param.server_context->trailing_metadata_, status); + param.call->PerformOps(&ops); + param.call->cq()->Pluck(&ops); + } + + private: + // Application provided rpc handler function. + std::function<Status(ServiceType*, ServerContext*, const RequestType*, + ResponseType*)> func_; + // The class the above handler function lives in. + ServiceType* service_; +}; + +// A wrapper class of an application provided client streaming handler. +template <class ServiceType, class RequestType, class ResponseType> +class ClientStreamingHandler : public MethodHandler { + public: + ClientStreamingHandler( + std::function<Status(ServiceType*, ServerContext*, + ServerReader<RequestType>*, ResponseType*)> func, + ServiceType* service) + : func_(func), service_(service) {} + + void RunHandler(const HandlerParameter& param) GRPC_FINAL { + ServerReader<RequestType> reader(param.call, param.server_context); + ResponseType rsp; + Status status = func_(service_, param.server_context, &reader, &rsp); + + GPR_ASSERT(!param.server_context->sent_initial_metadata_); + CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, + CallOpServerSendStatus> ops; + ops.SendInitialMetadata(param.server_context->initial_metadata_); + if (status.ok()) { + status = ops.SendMessage(rsp); + } + ops.ServerSendStatus(param.server_context->trailing_metadata_, status); + param.call->PerformOps(&ops); + param.call->cq()->Pluck(&ops); + } + + private: + std::function<Status(ServiceType*, ServerContext*, ServerReader<RequestType>*, + ResponseType*)> func_; + ServiceType* service_; +}; + +// A wrapper class of an application provided server streaming handler. +template <class ServiceType, class RequestType, class ResponseType> +class ServerStreamingHandler : public MethodHandler { + public: + ServerStreamingHandler( + std::function<Status(ServiceType*, ServerContext*, const RequestType*, + ServerWriter<ResponseType>*)> func, + ServiceType* service) + : func_(func), service_(service) {} + + void RunHandler(const HandlerParameter& param) GRPC_FINAL { + RequestType req; + Status status = SerializationTraits<RequestType>::Deserialize( + param.request, &req, param.max_message_size); + + if (status.ok()) { + ServerWriter<ResponseType> writer(param.call, param.server_context); + status = func_(service_, param.server_context, &req, &writer); + } + + CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops; + if (!param.server_context->sent_initial_metadata_) { + ops.SendInitialMetadata(param.server_context->initial_metadata_); + } + ops.ServerSendStatus(param.server_context->trailing_metadata_, status); + param.call->PerformOps(&ops); + param.call->cq()->Pluck(&ops); + } + + private: + std::function<Status(ServiceType*, ServerContext*, const RequestType*, + ServerWriter<ResponseType>*)> func_; + ServiceType* service_; +}; + +// A wrapper class of an application provided bidi-streaming handler. +template <class ServiceType, class RequestType, class ResponseType> +class BidiStreamingHandler : public MethodHandler { + public: + BidiStreamingHandler( + std::function<Status(ServiceType*, ServerContext*, + ServerReaderWriter<ResponseType, RequestType>*)> + func, + ServiceType* service) + : func_(func), service_(service) {} + + void RunHandler(const HandlerParameter& param) GRPC_FINAL { + ServerReaderWriter<ResponseType, RequestType> stream(param.call, + param.server_context); + Status status = func_(service_, param.server_context, &stream); + + CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops; + if (!param.server_context->sent_initial_metadata_) { + ops.SendInitialMetadata(param.server_context->initial_metadata_); + } + ops.ServerSendStatus(param.server_context->trailing_metadata_, status); + param.call->PerformOps(&ops); + param.call->cq()->Pluck(&ops); + } + + private: + std::function<Status(ServiceType*, ServerContext*, + ServerReaderWriter<ResponseType, RequestType>*)> func_; + ServiceType* service_; +}; + +// Handle unknown method by returning UNIMPLEMENTED error. +class UnknownMethodHandler : public MethodHandler { + public: + template <class T> + static void FillOps(ServerContext* context, T* ops) { + Status status(StatusCode::UNIMPLEMENTED, ""); + if (!context->sent_initial_metadata_) { + ops->SendInitialMetadata(context->initial_metadata_); + context->sent_initial_metadata_ = true; + } + ops->ServerSendStatus(context->trailing_metadata_, status); + } + + void RunHandler(const HandlerParameter& param) GRPC_FINAL { + CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops; + FillOps(param.server_context, &ops); + param.call->PerformOps(&ops); + param.call->cq()->Pluck(&ops); + } +}; + +} // namespace grpc + +#endif // GRPCXX_IMPL_METHOD_HANDLER_IMPL_H
\ No newline at end of file diff --git a/include/grpc++/impl/rpc_service_method.h b/include/grpc++/impl/rpc_service_method.h index b203c8f53a..a0bae80dab 100644 --- a/include/grpc++/impl/rpc_service_method.h +++ b/include/grpc++/impl/rpc_service_method.h @@ -1,6 +1,6 @@ /* * - * Copyright 2015, Google Inc. + * Copyright 2016, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,14 +43,11 @@ #include <grpc++/impl/rpc_method.h> #include <grpc++/support/config.h> #include <grpc++/support/status.h> -#include <grpc++/support/sync_stream.h> namespace grpc { class ServerContext; class StreamContextInterface; -// TODO(rocking): we might need to split this file into multiple ones. - // Base class for running an RPC handler. class MethodHandler { public: @@ -71,197 +68,25 @@ class MethodHandler { virtual void RunHandler(const HandlerParameter& param) = 0; }; -// A wrapper class of an application provided rpc method handler. -template <class ServiceType, class RequestType, class ResponseType> -class RpcMethodHandler : public MethodHandler { - public: - RpcMethodHandler( - std::function<Status(ServiceType*, ServerContext*, const RequestType*, - ResponseType*)> func, - ServiceType* service) - : func_(func), service_(service) {} - - void RunHandler(const HandlerParameter& param) GRPC_FINAL { - RequestType req; - Status status = SerializationTraits<RequestType>::Deserialize( - param.request, &req, param.max_message_size); - ResponseType rsp; - if (status.ok()) { - status = func_(service_, param.server_context, &req, &rsp); - } - - GPR_ASSERT(!param.server_context->sent_initial_metadata_); - CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, - CallOpServerSendStatus> ops; - ops.SendInitialMetadata(param.server_context->initial_metadata_); - if (status.ok()) { - status = ops.SendMessage(rsp); - } - ops.ServerSendStatus(param.server_context->trailing_metadata_, status); - param.call->PerformOps(&ops); - param.call->cq()->Pluck(&ops); - } - - private: - // Application provided rpc handler function. - std::function<Status(ServiceType*, ServerContext*, const RequestType*, - ResponseType*)> func_; - // The class the above handler function lives in. - ServiceType* service_; -}; - -// A wrapper class of an application provided client streaming handler. -template <class ServiceType, class RequestType, class ResponseType> -class ClientStreamingHandler : public MethodHandler { - public: - ClientStreamingHandler( - std::function<Status(ServiceType*, ServerContext*, - ServerReader<RequestType>*, ResponseType*)> func, - ServiceType* service) - : func_(func), service_(service) {} - - void RunHandler(const HandlerParameter& param) GRPC_FINAL { - ServerReader<RequestType> reader(param.call, param.server_context); - ResponseType rsp; - Status status = func_(service_, param.server_context, &reader, &rsp); - - GPR_ASSERT(!param.server_context->sent_initial_metadata_); - CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, - CallOpServerSendStatus> ops; - ops.SendInitialMetadata(param.server_context->initial_metadata_); - if (status.ok()) { - status = ops.SendMessage(rsp); - } - ops.ServerSendStatus(param.server_context->trailing_metadata_, status); - param.call->PerformOps(&ops); - param.call->cq()->Pluck(&ops); - } - - private: - std::function<Status(ServiceType*, ServerContext*, ServerReader<RequestType>*, - ResponseType*)> func_; - ServiceType* service_; -}; - -// A wrapper class of an application provided server streaming handler. -template <class ServiceType, class RequestType, class ResponseType> -class ServerStreamingHandler : public MethodHandler { - public: - ServerStreamingHandler( - std::function<Status(ServiceType*, ServerContext*, const RequestType*, - ServerWriter<ResponseType>*)> func, - ServiceType* service) - : func_(func), service_(service) {} - - void RunHandler(const HandlerParameter& param) GRPC_FINAL { - RequestType req; - Status status = SerializationTraits<RequestType>::Deserialize( - param.request, &req, param.max_message_size); - - if (status.ok()) { - ServerWriter<ResponseType> writer(param.call, param.server_context); - status = func_(service_, param.server_context, &req, &writer); - } - - CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops; - if (!param.server_context->sent_initial_metadata_) { - ops.SendInitialMetadata(param.server_context->initial_metadata_); - } - ops.ServerSendStatus(param.server_context->trailing_metadata_, status); - param.call->PerformOps(&ops); - param.call->cq()->Pluck(&ops); - } - - private: - std::function<Status(ServiceType*, ServerContext*, const RequestType*, - ServerWriter<ResponseType>*)> func_; - ServiceType* service_; -}; - -// A wrapper class of an application provided bidi-streaming handler. -template <class ServiceType, class RequestType, class ResponseType> -class BidiStreamingHandler : public MethodHandler { - public: - BidiStreamingHandler( - std::function<Status(ServiceType*, ServerContext*, - ServerReaderWriter<ResponseType, RequestType>*)> - func, - ServiceType* service) - : func_(func), service_(service) {} - - void RunHandler(const HandlerParameter& param) GRPC_FINAL { - ServerReaderWriter<ResponseType, RequestType> stream(param.call, - param.server_context); - Status status = func_(service_, param.server_context, &stream); - - CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops; - if (!param.server_context->sent_initial_metadata_) { - ops.SendInitialMetadata(param.server_context->initial_metadata_); - } - ops.ServerSendStatus(param.server_context->trailing_metadata_, status); - param.call->PerformOps(&ops); - param.call->cq()->Pluck(&ops); - } - - private: - std::function<Status(ServiceType*, ServerContext*, - ServerReaderWriter<ResponseType, RequestType>*)> func_; - ServiceType* service_; -}; - -// Handle unknown method by returning UNIMPLEMENTED error. -class UnknownMethodHandler : public MethodHandler { - public: - template <class T> - static void FillOps(ServerContext* context, T* ops) { - Status status(StatusCode::UNIMPLEMENTED, ""); - if (!context->sent_initial_metadata_) { - ops->SendInitialMetadata(context->initial_metadata_); - context->sent_initial_metadata_ = true; - } - ops->ServerSendStatus(context->trailing_metadata_, status); - } - - void RunHandler(const HandlerParameter& param) GRPC_FINAL { - CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops; - FillOps(param.server_context, &ops); - param.call->PerformOps(&ops); - param.call->cq()->Pluck(&ops); - } -}; - // Server side rpc method class class RpcServiceMethod : public RpcMethod { public: // Takes ownership of the handler RpcServiceMethod(const char* name, RpcMethod::RpcType type, MethodHandler* handler) - : RpcMethod(name, type), handler_(handler) {} + : RpcMethod(name, type), server_tag_(nullptr), handler_(handler) {} - MethodHandler* handler() { return handler_.get(); } + void set_server_tag(void* tag) { server_tag_ = tag; } + void* server_tag() const { return server_tag_; } + // if MethodHandler is nullptr, then this is an async method + MethodHandler* handler() const { return handler_.get(); } + void ResetHandler() { handler_.reset(); } private: + void* server_tag_; std::unique_ptr<MethodHandler> handler_; }; -// This class contains all the method information for an rpc service. It is -// used for registering a service on a grpc server. -class RpcService { - public: - // Takes ownership. - void AddMethod(RpcServiceMethod* method) { methods_.emplace_back(method); } - - RpcServiceMethod* GetMethod(int i) { return methods_[i].get(); } - int GetMethodCount() const { - // On win x64, int is only 32bit - GPR_ASSERT(methods_.size() <= INT_MAX); - return (int)methods_.size(); - } - - private: - std::vector<std::unique_ptr<RpcServiceMethod>> methods_; -}; - } // namespace grpc #endif // GRPCXX_IMPL_RPC_SERVICE_METHOD_H diff --git a/include/grpc++/impl/service_type.h b/include/grpc++/impl/service_type.h index 3b6ac1de77..483ecb32b0 100644 --- a/include/grpc++/impl/service_type.h +++ b/include/grpc++/impl/service_type.h @@ -1,6 +1,6 @@ /* * - * Copyright 2015, Google Inc. + * Copyright 2015-2016, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,6 +34,7 @@ #ifndef GRPCXX_IMPL_SERVICE_TYPE_H #define GRPCXX_IMPL_SERVICE_TYPE_H +#include <grpc++/impl/rpc_service_method.h> #include <grpc++/impl/serialization_traits.h> #include <grpc++/server.h> #include <grpc++/support/config.h> @@ -43,17 +44,10 @@ namespace grpc { class Call; class CompletionQueue; -class RpcService; class Server; class ServerCompletionQueue; class ServerContext; -class SynchronousService { - public: - virtual ~SynchronousService() {} - virtual RpcService* service() = 0; -}; - class ServerAsyncStreamingInterface { public: virtual ~ServerAsyncStreamingInterface() {} @@ -65,15 +59,37 @@ class ServerAsyncStreamingInterface { virtual void BindCall(Call* call) = 0; }; -class AsynchronousService { +class Service { public: - AsynchronousService(const char** method_names, size_t method_count) - : server_(nullptr), - method_names_(method_names), - method_count_(method_count), - request_args_(nullptr) {} + Service() : server_(nullptr) {} + virtual ~Service() {} + + bool has_async_methods() const { + for (auto it = methods_.begin(); it != methods_.end(); ++it) { + if (*it && (*it)->handler() == nullptr) { + return true; + } + } + return false; + } + + bool has_synchronous_methods() const { + for (auto it = methods_.begin(); it != methods_.end(); ++it) { + if (*it && (*it)->handler() != nullptr) { + return true; + } + } + return false; + } - ~AsynchronousService() { delete[] request_args_; } + bool has_generic_methods() const { + for (auto it = methods_.begin(); it != methods_.end(); ++it) { + if (it->get() == nullptr) { + return true; + } + } + return false; + } protected: template <class Message> @@ -81,41 +97,58 @@ class AsynchronousService { ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, ServerCompletionQueue* notification_cq, void* tag) { - server_->RequestAsyncCall(request_args_[index], context, stream, call_cq, + server_->RequestAsyncCall(methods_[index].get(), context, stream, call_cq, notification_cq, tag, request); } - void RequestClientStreaming(int index, ServerContext* context, - ServerAsyncStreamingInterface* stream, - CompletionQueue* call_cq, - ServerCompletionQueue* notification_cq, - void* tag) { - server_->RequestAsyncCall(request_args_[index], context, stream, call_cq, + void RequestAsyncClientStreaming(int index, ServerContext* context, + ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, + void* tag) { + server_->RequestAsyncCall(methods_[index].get(), context, stream, call_cq, notification_cq, tag); } template <class Message> - void RequestServerStreaming(int index, ServerContext* context, - Message* request, - ServerAsyncStreamingInterface* stream, - CompletionQueue* call_cq, - ServerCompletionQueue* notification_cq, - void* tag) { - server_->RequestAsyncCall(request_args_[index], context, stream, call_cq, + void RequestAsyncServerStreaming(int index, ServerContext* context, + Message* request, + ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, + void* tag) { + server_->RequestAsyncCall(methods_[index].get(), context, stream, call_cq, notification_cq, tag, request); } - void RequestBidiStreaming(int index, ServerContext* context, - ServerAsyncStreamingInterface* stream, - CompletionQueue* call_cq, - ServerCompletionQueue* notification_cq, void* tag) { - server_->RequestAsyncCall(request_args_[index], context, stream, call_cq, + void RequestAsyncBidiStreaming(int index, ServerContext* context, + ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, + void* tag) { + server_->RequestAsyncCall(methods_[index].get(), context, stream, call_cq, notification_cq, tag); } + void AddMethod(RpcServiceMethod* method) { methods_.emplace_back(method); } + + void MarkMethodAsync(int index) { + if (methods_[index].get() == nullptr) { + gpr_log(GPR_ERROR, "Method already marked generic."); + return; + } + methods_[index]->ResetHandler(); + } + + void MarkMethodGeneric(int index) { + if (methods_[index]->handler() == nullptr) { + gpr_log(GPR_ERROR, "Method already marked async."); + } + methods_[index].reset(); + } + private: friend class Server; + Server* server_; - const char** const method_names_; - size_t method_count_; - void** request_args_; + std::vector<std::unique_ptr<RpcServiceMethod>> methods_; }; } // namespace grpc diff --git a/include/grpc++/server.h b/include/grpc++/server.h index 644e66e6e0..18eb4b91b0 100644 --- a/include/grpc++/server.h +++ b/include/grpc++/server.h @@ -1,6 +1,6 @@ /* * - * Copyright 2015, Google Inc. + * Copyright 2015-2016, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,6 +40,7 @@ #include <grpc++/completion_queue.h> #include <grpc++/impl/call.h> #include <grpc++/impl/grpc_library.h> +#include <grpc++/impl/rpc_service_method.h> #include <grpc++/impl/sync.h> #include <grpc++/security/server_credentials.h> #include <grpc++/support/channel_arguments.h> @@ -51,13 +52,11 @@ struct grpc_server; namespace grpc { -class AsynchronousService; class GenericServerContext; class AsyncGenericService; -class RpcService; -class RpcServiceMethod; class ServerAsyncStreamingInterface; class ServerContext; +class Service; class ThreadPoolInterface; /// Models a gRPC server. @@ -105,7 +104,7 @@ class Server GRPC_FINAL : public GrpcLibrary, private CallHook { private: friend class AsyncGenericService; - friend class AsynchronousService; + friend class Service; friend class ServerBuilder; class SyncRequest; @@ -123,12 +122,7 @@ class Server GRPC_FINAL : public GrpcLibrary, private CallHook { /// Register a service. This call does not take ownership of the service. /// The service must exist for the lifetime of the Server instance. - bool RegisterService(const grpc::string* host, RpcService* service); - - /// Register an asynchronous service. This call does not take ownership of the - /// service. The service must exist for the lifetime of the Server instance. - bool RegisterAsyncService(const grpc::string* host, - AsynchronousService* service); + bool RegisterService(const grpc::string* host, Service* service); /// Register a generic service. This call does not take ownership of the /// service. The service must exist for the lifetime of the Server instance. @@ -265,21 +259,24 @@ class Server GRPC_FINAL : public GrpcLibrary, private CallHook { class UnimplementedAsyncResponse; template <class Message> - void RequestAsyncCall(void* registered_method, ServerContext* context, + void RequestAsyncCall(RpcServiceMethod* method, ServerContext* context, ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, ServerCompletionQueue* notification_cq, void* tag, Message* message) { - new PayloadAsyncRequest<Message>(registered_method, this, context, stream, - call_cq, notification_cq, tag, message); + GPR_ASSERT(method); + new PayloadAsyncRequest<Message>(method->server_tag(), this, context, + stream, call_cq, notification_cq, tag, + message); } - void RequestAsyncCall(void* registered_method, ServerContext* context, + void RequestAsyncCall(RpcServiceMethod* method, ServerContext* context, ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, ServerCompletionQueue* notification_cq, void* tag) { - new NoPayloadAsyncRequest(registered_method, this, context, stream, call_cq, - notification_cq, tag); + GPR_ASSERT(method); + new NoPayloadAsyncRequest(method->server_tag(), this, context, stream, + call_cq, notification_cq, tag); } void RequestAsyncGenericCall(GenericServerContext* context, diff --git a/include/grpc++/server_builder.h b/include/grpc++/server_builder.h index b324deb9e0..73bcfb6038 100644 --- a/include/grpc++/server_builder.h +++ b/include/grpc++/server_builder.h @@ -1,6 +1,6 @@ /* * - * Copyright 2015, Google Inc. + * Copyright 2015-2016, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,14 +44,12 @@ namespace grpc { class AsyncGenericService; -class AsynchronousService; class CompletionQueue; class RpcService; class Server; class ServerCompletionQueue; class ServerCredentials; -class SynchronousService; -class ThreadPoolInterface; +class Service; /// A builder class for the creation and startup of \a grpc::Server instances. class ServerBuilder { @@ -62,14 +60,7 @@ class ServerBuilder { /// The service must exist for the lifetime of the \a Server instance returned /// by \a BuildAndStart(). /// Matches requests with any :authority - void RegisterService(SynchronousService* service); - - /// Register an asynchronous service. - /// This call does not take ownership of the service or completion queue. - /// The service and completion queuemust exist for the lifetime of the \a - /// Server instance returned by \a BuildAndStart(). - /// Matches requests with any :authority - void RegisterAsyncService(AsynchronousService* service); + void RegisterService(Service* service); /// Register a generic service. /// Matches requests with any :authority @@ -79,15 +70,7 @@ class ServerBuilder { /// The service must exist for the lifetime of the \a Server instance returned /// by BuildAndStart(). /// Only matches requests with :authority \a host - void RegisterService(const grpc::string& host, SynchronousService* service); - - /// Register an asynchronous service. - /// This call does not take ownership of the service or completion queue. - /// The service and completion queuemust exist for the lifetime of the \a - /// Server instance returned by \a BuildAndStart(). - /// Only matches requests with :authority equal to \a host - void RegisterAsyncService(const grpc::string& host, - AsynchronousService* service); + void RegisterService(const grpc::string& host, Service* service); /// Set max message size in bytes. void SetMaxMessageSize(int max_message_size) { @@ -132,26 +115,22 @@ class ServerBuilder { }; typedef std::unique_ptr<grpc::string> HostString; - template <class T> struct NamedService { - explicit NamedService(T* s) : service(s) {} - NamedService(const grpc::string& h, T* s) + explicit NamedService(Service* s) : service(s) {} + NamedService(const grpc::string& h, Service* s) : host(new grpc::string(h)), service(s) {} HostString host; - T* service; + Service* service; }; int max_message_size_; grpc_compression_options compression_options_; std::vector<std::unique_ptr<ServerBuilderOption>> options_; - std::vector<std::unique_ptr<NamedService<RpcService>>> services_; - std::vector<std::unique_ptr<NamedService<AsynchronousService>>> - async_services_; + std::vector<std::unique_ptr<NamedService>> services_; std::vector<Port> ports_; std::vector<ServerCompletionQueue*> cqs_; std::shared_ptr<ServerCredentials> creds_; AsyncGenericService* generic_service_; - ThreadPoolInterface* thread_pool_; }; } // namespace grpc diff --git a/src/compiler/cpp_generator.cc b/src/compiler/cpp_generator.cc index 3c8ca8ab45..c860148269 100644 --- a/src/compiler/cpp_generator.cc +++ b/src/compiler/cpp_generator.cc @@ -1,6 +1,6 @@ /* * - * Copyright 2015, Google Inc. + * Copyright 2015-2016, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -491,39 +491,186 @@ void PrintHeaderServerMethodAsync( grpc_cpp_generator::ClassName(method->input_type(), true); (*vars)["Response"] = grpc_cpp_generator::ClassName(method->output_type(), true); + printer->Print(*vars, "template <class BaseClass>\n"); + printer->Print(*vars, + "class WithAsyncMethod_$Method$ : public BaseClass {\n"); + printer->Print( + " private:\n" + " void BaseClassMustBeDerivedFromService(Service *service) {}\n"); + printer->Print(" public:\n"); + printer->Indent(); + printer->Print(*vars, + "WithAsyncMethod_$Method$() {\n" + " ::grpc::Service::MarkMethodAsync($Idx$);\n" + "}\n"); + printer->Print(*vars, + "~WithAsyncMethod_$Method$() GRPC_OVERRIDE {\n" + " BaseClassMustBeDerivedFromService(this);\n" + "}\n"); if (NoStreaming(method)) { printer->Print( *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "$Response$* response) GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print( + *vars, "void Request$Method$(" "::grpc::ServerContext* context, $Request$* request, " "::grpc::ServerAsyncResponseWriter< $Response$>* response, " "::grpc::CompletionQueue* new_call_cq, " - "::grpc::ServerCompletionQueue* notification_cq, void *tag);\n"); + "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); + printer->Print(*vars, + " ::grpc::Service::RequestAsyncUnary($Idx$, context, " + "request, response, new_call_cq, notification_cq, tag);\n"); + printer->Print("}\n"); } else if (ClientOnlyStreaming(method)) { printer->Print( *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReader< $Request$>* reader, " + "$Response$* response) GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print( + *vars, "void Request$Method$(" "::grpc::ServerContext* context, " "::grpc::ServerAsyncReader< $Response$, $Request$>* reader, " "::grpc::CompletionQueue* new_call_cq, " - "::grpc::ServerCompletionQueue* notification_cq, void *tag);\n"); + "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); + printer->Print(*vars, + " ::grpc::Service::RequestAsyncClientStreaming($Idx$, " + "context, reader, new_call_cq, notification_cq, tag);\n"); + printer->Print("}\n"); } else if (ServerOnlyStreaming(method)) { printer->Print( *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "::grpc::ServerWriter< $Response$>* writer) GRPC_FINAL GRPC_OVERRIDE " + "{\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print( + *vars, "void Request$Method$(" "::grpc::ServerContext* context, $Request$* request, " "::grpc::ServerAsyncWriter< $Response$>* writer, " "::grpc::CompletionQueue* new_call_cq, " - "::grpc::ServerCompletionQueue* notification_cq, void *tag);\n"); + "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); + printer->Print( + *vars, + " ::grpc::Service::RequestAsyncServerStreaming($Idx$, " + "context, request, writer, new_call_cq, notification_cq, tag);\n"); + printer->Print("}\n"); } else if (BidiStreaming(method)) { printer->Print( *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReaderWriter< $Response$, $Request$>* stream) " + "GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print( + *vars, "void Request$Method$(" "::grpc::ServerContext* context, " "::grpc::ServerAsyncReaderWriter< $Response$, $Request$>* stream, " "::grpc::CompletionQueue* new_call_cq, " - "::grpc::ServerCompletionQueue* notification_cq, void *tag);\n"); + "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); + printer->Print(*vars, + " ::grpc::Service::RequestAsyncBidiStreaming($Idx$, " + "context, stream, new_call_cq, notification_cq, tag);\n"); + printer->Print("}\n"); } + printer->Outdent(); + printer->Print(*vars, "};\n"); +} + +void PrintHeaderServerMethodGeneric( + grpc::protobuf::io::Printer *printer, + const grpc::protobuf::MethodDescriptor *method, + std::map<grpc::string, grpc::string> *vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = + grpc_cpp_generator::ClassName(method->input_type(), true); + (*vars)["Response"] = + grpc_cpp_generator::ClassName(method->output_type(), true); + printer->Print(*vars, "template <class BaseClass>\n"); + printer->Print(*vars, + "class WithGenericMethod_$Method$ : public BaseClass {\n"); + printer->Print( + " private:\n" + " void BaseClassMustBeDerivedFromService(Service *service) {}\n"); + printer->Print(" public:\n"); + printer->Indent(); + printer->Print(*vars, + "WithGenericMethod_$Method$() {\n" + " ::grpc::Service::MarkMethodGeneric($Idx$);\n" + "}\n"); + printer->Print(*vars, + "~WithGenericMethod_$Method$() GRPC_OVERRIDE {\n" + " BaseClassMustBeDerivedFromService(this);\n" + "}\n"); + if (NoStreaming(method)) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "$Response$* response) GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReader< $Request$>* reader, " + "$Response$* response) GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "::grpc::ServerWriter< $Response$>* writer) GRPC_FINAL GRPC_OVERRIDE " + "{\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + } else if (BidiStreaming(method)) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReaderWriter< $Response$, $Request$>* stream) " + "GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + } + printer->Outdent(); + printer->Print(*vars, "};\n"); } void PrintHeaderService(grpc::protobuf::io::Printer *printer, @@ -580,9 +727,9 @@ void PrintHeaderService(grpc::protobuf::io::Printer *printer, printer->Print("\n"); - // Server side - Synchronous + // Server side - base printer->Print( - "class Service : public ::grpc::SynchronousService {\n" + "class Service : public ::grpc::Service {\n" " public:\n"); printer->Indent(); printer->Print("Service();\n"); @@ -590,26 +737,32 @@ void PrintHeaderService(grpc::protobuf::io::Printer *printer, for (int i = 0; i < service->method_count(); ++i) { PrintHeaderServerMethodSync(printer, service->method(i), vars); } - printer->Print("::grpc::RpcService* service() GRPC_OVERRIDE GRPC_FINAL;\n"); printer->Outdent(); - printer->Print( - " private:\n" - " std::unique_ptr< ::grpc::RpcService> service_;\n"); printer->Print("};\n"); // Server side - Asynchronous - printer->Print( - "class AsyncService GRPC_FINAL : public ::grpc::AsynchronousService {\n" - " public:\n"); - printer->Indent(); - (*vars)["MethodCount"] = as_string(service->method_count()); - printer->Print("explicit AsyncService();\n"); - printer->Print("~AsyncService() {};\n"); for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); PrintHeaderServerMethodAsync(printer, service->method(i), vars); } - printer->Outdent(); - printer->Print("};\n"); + + printer->Print("typedef "); + + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["method_name"] = service->method(i)->name(); + printer->Print(*vars, "WithAsyncMethod_$method_name$<"); + } + printer->Print("Service"); + for (int i = 0; i < service->method_count(); ++i) { + printer->Print(" >"); + } + printer->Print(" AsyncService;\n"); + + // Server side - Generic + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); + PrintHeaderServerMethodGeneric(printer, service->method(i), vars); + } printer->Outdent(); printer->Print("};\n"); @@ -623,6 +776,12 @@ grpc::string GetHeaderServices(const grpc::protobuf::FileDescriptor *file, grpc::protobuf::io::StringOutputStream output_stream(&output); grpc::protobuf::io::Printer printer(&output_stream, '$'); std::map<grpc::string, grpc::string> vars; + // Package string is empty or ends with a dot. It is used to fully qualify + // method names. + vars["Package"] = file->package(); + if (!file->package().empty()) { + vars["Package"].append("."); + } if (!params.services_namespace.empty()) { vars["services_namespace"] = params.services_namespace; @@ -704,6 +863,7 @@ grpc::string GetSourceIncludes(const grpc::protobuf::FileDescriptor *file, printer.Print(vars, "#include <grpc++/channel.h>\n"); printer.Print(vars, "#include <grpc++/impl/client_unary_call.h>\n"); + printer.Print(vars, "#include <grpc++/impl/method_handler_impl.h>\n"); printer.Print(vars, "#include <grpc++/impl/rpc_service_method.h>\n"); printer.Print(vars, "#include <grpc++/impl/service_type.h>\n"); printer.Print(vars, "#include <grpc++/support/async_unary_call.h>\n"); @@ -889,69 +1049,6 @@ void PrintSourceServerMethod(grpc::protobuf::io::Printer *printer, } } -void PrintSourceServerAsyncMethod( - grpc::protobuf::io::Printer *printer, - const grpc::protobuf::MethodDescriptor *method, - std::map<grpc::string, grpc::string> *vars) { - (*vars)["Method"] = method->name(); - (*vars)["Request"] = - grpc_cpp_generator::ClassName(method->input_type(), true); - (*vars)["Response"] = - grpc_cpp_generator::ClassName(method->output_type(), true); - if (NoStreaming(method)) { - printer->Print( - *vars, - "void $ns$$Service$::AsyncService::Request$Method$(" - "::grpc::ServerContext* context, " - "$Request$* request, " - "::grpc::ServerAsyncResponseWriter< $Response$>* response, " - "::grpc::CompletionQueue* new_call_cq, " - "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); - printer->Print(*vars, - " AsynchronousService::RequestAsyncUnary($Idx$, context, " - "request, response, new_call_cq, notification_cq, tag);\n"); - printer->Print("}\n\n"); - } else if (ClientOnlyStreaming(method)) { - printer->Print( - *vars, - "void $ns$$Service$::AsyncService::Request$Method$(" - "::grpc::ServerContext* context, " - "::grpc::ServerAsyncReader< $Response$, $Request$>* reader, " - "::grpc::CompletionQueue* new_call_cq, " - "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); - printer->Print(*vars, - " AsynchronousService::RequestClientStreaming($Idx$, " - "context, reader, new_call_cq, notification_cq, tag);\n"); - printer->Print("}\n\n"); - } else if (ServerOnlyStreaming(method)) { - printer->Print( - *vars, - "void $ns$$Service$::AsyncService::Request$Method$(" - "::grpc::ServerContext* context, " - "$Request$* request, " - "::grpc::ServerAsyncWriter< $Response$>* writer, " - "::grpc::CompletionQueue* new_call_cq, " - "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); - printer->Print( - *vars, - " AsynchronousService::RequestServerStreaming($Idx$, " - "context, request, writer, new_call_cq, notification_cq, tag);\n"); - printer->Print("}\n\n"); - } else if (BidiStreaming(method)) { - printer->Print( - *vars, - "void $ns$$Service$::AsyncService::Request$Method$(" - "::grpc::ServerContext* context, " - "::grpc::ServerAsyncReaderWriter< $Response$, $Request$>* stream, " - "::grpc::CompletionQueue* new_call_cq, " - "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); - printer->Print(*vars, - " AsynchronousService::RequestBidiStreaming($Idx$, " - "context, stream, new_call_cq, notification_cq, tag);\n"); - printer->Print("}\n\n"); - } -} - void PrintSourceService(grpc::protobuf::io::Printer *printer, const grpc::protobuf::ServiceDescriptor *service, std::map<grpc::string, grpc::string> *vars) { @@ -1006,32 +1103,8 @@ void PrintSourceService(grpc::protobuf::io::Printer *printer, PrintSourceClientMethod(printer, service->method(i), vars); } - (*vars)["MethodCount"] = as_string(service->method_count()); - printer->Print(*vars, - "$ns$$Service$::AsyncService::AsyncService() : " - "::grpc::AsynchronousService(" - "$prefix$$Service$_method_names, $MethodCount$) " - "{}\n\n"); - - printer->Print(*vars, - "$ns$$Service$::Service::Service() {\n" - "}\n\n"); - printer->Print(*vars, - "$ns$$Service$::Service::~Service() {\n" - "}\n\n"); - for (int i = 0; i < service->method_count(); ++i) { - (*vars)["Idx"] = as_string(i); - PrintSourceServerMethod(printer, service->method(i), vars); - PrintSourceServerAsyncMethod(printer, service->method(i), vars); - } - printer->Print(*vars, - "::grpc::RpcService* $ns$$Service$::Service::service() {\n"); + printer->Print(*vars, "$ns$$Service$::Service::Service() {\n"); printer->Indent(); - printer->Print( - "if (service_) {\n" - " return service_.get();\n" - "}\n"); - printer->Print("service_ = std::unique_ptr< ::grpc::RpcService>(new ::grpc::RpcService());\n"); for (int i = 0; i < service->method_count(); ++i) { const grpc::protobuf::MethodDescriptor *method = service->method(i); (*vars)["Idx"] = as_string(i); @@ -1043,7 +1116,7 @@ void PrintSourceService(grpc::protobuf::io::Printer *printer, if (NoStreaming(method)) { printer->Print( *vars, - "service_->AddMethod(new ::grpc::RpcServiceMethod(\n" + "AddMethod(new ::grpc::RpcServiceMethod(\n" " $prefix$$Service$_method_names[$Idx$],\n" " ::grpc::RpcMethod::NORMAL_RPC,\n" " new ::grpc::RpcMethodHandler< $ns$$Service$::Service, " @@ -1053,7 +1126,7 @@ void PrintSourceService(grpc::protobuf::io::Printer *printer, } else if (ClientOnlyStreaming(method)) { printer->Print( *vars, - "service_->AddMethod(new ::grpc::RpcServiceMethod(\n" + "AddMethod(new ::grpc::RpcServiceMethod(\n" " $prefix$$Service$_method_names[$Idx$],\n" " ::grpc::RpcMethod::CLIENT_STREAMING,\n" " new ::grpc::ClientStreamingHandler< " @@ -1062,7 +1135,7 @@ void PrintSourceService(grpc::protobuf::io::Printer *printer, } else if (ServerOnlyStreaming(method)) { printer->Print( *vars, - "service_->AddMethod(new ::grpc::RpcServiceMethod(\n" + "AddMethod(new ::grpc::RpcServiceMethod(\n" " $prefix$$Service$_method_names[$Idx$],\n" " ::grpc::RpcMethod::SERVER_STREAMING,\n" " new ::grpc::ServerStreamingHandler< " @@ -1071,7 +1144,7 @@ void PrintSourceService(grpc::protobuf::io::Printer *printer, } else if (BidiStreaming(method)) { printer->Print( *vars, - "service_->AddMethod(new ::grpc::RpcServiceMethod(\n" + "AddMethod(new ::grpc::RpcServiceMethod(\n" " $prefix$$Service$_method_names[$Idx$],\n" " ::grpc::RpcMethod::BIDI_STREAMING,\n" " new ::grpc::BidiStreamingHandler< " @@ -1079,9 +1152,15 @@ void PrintSourceService(grpc::protobuf::io::Printer *printer, " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); } } - printer->Print("return service_.get();\n"); printer->Outdent(); - printer->Print("}\n\n"); + printer->Print(*vars, "}\n\n"); + printer->Print(*vars, + "$ns$$Service$::Service::~Service() {\n" + "}\n\n"); + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); + PrintSourceServerMethod(printer, service->method(i), vars); + } } grpc::string GetSourceServices(const grpc::protobuf::FileDescriptor *file, diff --git a/src/cpp/server/server.cc b/src/cpp/server/server.cc index 878775bbee..7bd7eb3f91 100644 --- a/src/cpp/server/server.cc +++ b/src/cpp/server/server.cc @@ -1,6 +1,6 @@ /* * - * Copyright 2015, Google Inc. + * Copyright 2015-2016, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,6 +40,7 @@ #include <grpc/support/log.h> #include <grpc++/completion_queue.h> #include <grpc++/generic/async_generic_service.h> +#include <grpc++/impl/method_handler_impl.h> #include <grpc++/impl/rpc_service_method.h> #include <grpc++/impl/service_type.h> #include <grpc++/server_context.h> @@ -296,6 +297,8 @@ Server::~Server() { if (started_ && !shutdown_) { lock.unlock(); Shutdown(); + } else if (!started_) { + cq_.Shutdown(); } } void* got_tag; @@ -314,36 +317,31 @@ void Server::SetGlobalCallbacks(GlobalCallbacks* callbacks) { g_callbacks = callbacks; } -bool Server::RegisterService(const grpc::string* host, RpcService* service) { - for (int i = 0; i < service->GetMethodCount(); ++i) { - RpcServiceMethod* method = service->GetMethod(i); +bool Server::RegisterService(const grpc::string* host, 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; + } + for (auto it = service->methods_.begin(); it != service->methods_.end(); + ++it) { + if (it->get() == nullptr) { // Handled by generic service if any. + continue; + } + RpcServiceMethod* method = it->get(); void* tag = grpc_server_register_method(server_, method->name(), host ? host->c_str() : nullptr); - if (!tag) { + if (tag == nullptr) { gpr_log(GPR_DEBUG, "Attempt to register %s multiple times", method->name()); return false; } - sync_methods_->emplace_back(method, tag); - } - return true; -} - -bool Server::RegisterAsyncService(const grpc::string* host, - AsynchronousService* service) { - GPR_ASSERT(service->server_ == nullptr && - "Can only register an asynchronous service against one server."); - service->server_ = this; - service->request_args_ = new void* [service->method_count_]; - for (size_t i = 0; i < service->method_count_; ++i) { - void* tag = grpc_server_register_method(server_, service->method_names_[i], - host ? host->c_str() : nullptr); - if (!tag) { - gpr_log(GPR_DEBUG, "Attempt to register %s multiple times", - service->method_names_[i]); - return false; + if (method->handler() == nullptr) { + method->set_server_tag(tag); + } else { + sync_methods_->emplace_back(method, tag); } - service->request_args_[i] = tag; } return true; } diff --git a/src/cpp/server/server_builder.cc b/src/cpp/server/server_builder.cc index 26c0724a30..a8c188e5a5 100644 --- a/src/cpp/server/server_builder.cc +++ b/src/cpp/server/server_builder.cc @@ -1,6 +1,6 @@ /* * - * Copyright 2015, Google Inc. + * Copyright 2015-2016, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,7 +43,7 @@ namespace grpc { ServerBuilder::ServerBuilder() - : max_message_size_(-1), generic_service_(nullptr), thread_pool_(nullptr) { + : max_message_size_(-1), generic_service_(nullptr) { grpc_compression_options_init(&compression_options_); } @@ -53,24 +53,13 @@ std::unique_ptr<ServerCompletionQueue> ServerBuilder::AddCompletionQueue() { return std::unique_ptr<ServerCompletionQueue>(cq); } -void ServerBuilder::RegisterService(SynchronousService* service) { - services_.emplace_back(new NamedService<RpcService>(service->service())); -} - -void ServerBuilder::RegisterAsyncService(AsynchronousService* service) { - async_services_.emplace_back(new NamedService<AsynchronousService>(service)); +void ServerBuilder::RegisterService(Service* service) { + services_.emplace_back(new NamedService(service)); } void ServerBuilder::RegisterService(const grpc::string& addr, - SynchronousService* service) { - services_.emplace_back( - new NamedService<RpcService>(addr, service->service())); -} - -void ServerBuilder::RegisterAsyncService(const grpc::string& addr, - AsynchronousService* service) { - async_services_.emplace_back( - new NamedService<AsynchronousService>(addr, service)); + Service* service) { + services_.emplace_back(new NamedService(addr, service)); } void ServerBuilder::RegisterAsyncGenericService(AsyncGenericService* service) { @@ -96,14 +85,14 @@ void ServerBuilder::AddListeningPort(const grpc::string& addr, } std::unique_ptr<Server> ServerBuilder::BuildAndStart() { - bool thread_pool_owned = false; - if (!async_services_.empty() && !services_.empty()) { - gpr_log(GPR_ERROR, "Mixing async and sync services is unsupported for now"); - return nullptr; - } - if (!thread_pool_ && !services_.empty()) { - thread_pool_ = CreateDefaultThreadPool(); - thread_pool_owned = true; + std::unique_ptr<ThreadPoolInterface> thread_pool; + for (auto it = services_.begin(); it != services_.end(); ++it) { + if ((*it)->service->has_synchronous_methods()) { + if (thread_pool == nullptr) { + thread_pool.reset(CreateDefaultThreadPool()); + break; + } + } } ChannelArguments args; for (auto option = options_.begin(); option != options_.end(); ++option) { @@ -115,7 +104,7 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() { args.SetInt(GRPC_COMPRESSION_ALGORITHM_STATE_ARG, compression_options_.enabled_algorithms_bitset); std::unique_ptr<Server> server( - new Server(thread_pool_, thread_pool_owned, max_message_size_, args)); + new Server(thread_pool.release(), true, max_message_size_, args)); for (auto cq = cqs_.begin(); cq != cqs_.end(); ++cq) { grpc_server_register_completion_queue(server->server_, (*cq)->cq(), nullptr); @@ -126,15 +115,17 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() { return nullptr; } } - for (auto service = async_services_.begin(); service != async_services_.end(); - service++) { - if (!server->RegisterAsyncService((*service)->host.get(), - (*service)->service)) { - return nullptr; - } - } if (generic_service_) { server->RegisterAsyncGenericService(generic_service_); + } else { + for (auto it = services_.begin(); it != services_.end(); ++it) { + if ((*it)->service->has_generic_methods()) { + gpr_log(GPR_ERROR, + "Some methods were marked generic but there is no " + "generic service registered."); + return nullptr; + } + } } for (auto port = ports_.begin(); port != ports_.end(); port++) { int r = server->AddListeningPort(port->addr, port->creds.get()); diff --git a/test/cpp/end2end/async_end2end_test.cc b/test/cpp/end2end/async_end2end_test.cc index cfda571326..0616cc07ee 100644 --- a/test/cpp/end2end/async_end2end_test.cc +++ b/test/cpp/end2end/async_end2end_test.cc @@ -180,21 +180,11 @@ class AsyncEnd2endTest : public ::testing::TestWithParam<bool> { int port = grpc_pick_unused_port_or_die(); server_address_ << "localhost:" << port; - // It is currently unsupported to mix sync and async services - // in the same server, so first test that (for coverage) - ServerBuilder build_bad; - build_bad.AddListeningPort(server_address_.str(), - grpc::InsecureServerCredentials()); - build_bad.RegisterAsyncService(&service_); - grpc::testing::EchoTestService::Service sync_service; - build_bad.RegisterService(&sync_service); - GPR_ASSERT(build_bad.BuildAndStart() == nullptr); - // Setup server ServerBuilder builder; builder.AddListeningPort(server_address_.str(), grpc::InsecureServerCredentials()); - builder.RegisterAsyncService(&service_); + builder.RegisterService(&service_); cq_ = builder.AddCompletionQueue(); server_ = builder.BuildAndStart(); } diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc index f8027bcf0b..58b20e61c9 100644 --- a/test/cpp/end2end/end2end_test.cc +++ b/test/cpp/end2end/end2end_test.cc @@ -54,6 +54,7 @@ #include "test/core/end2end/data/ssl_test_data.h" #include "test/core/util/port.h" #include "test/core/util/test_config.h" +#include "test/cpp/end2end/test_service_impl.h" #include "test/cpp/util/string_ref_helper.h" using grpc::testing::EchoRequest; @@ -64,40 +65,6 @@ namespace grpc { namespace testing { namespace { -const char* kServerCancelAfterReads = "cancel_after_reads"; - -// When echo_deadline is requested, deadline seen in the ServerContext is set in -// the response in seconds. -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_client_identity) { - std::shared_ptr<const AuthContext> auth_ctx = context->auth_context(); - std::vector<grpc::string_ref> ssl = - auth_ctx->FindPropertyValues("transport_security_type"); - EXPECT_EQ(1u, ssl.size()); - EXPECT_EQ("ssl", ToString(ssl[0])); - if (expected_client_identity.length() == 0) { - 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]); - } -} - bool CheckIsLocalhost(const grpc::string& addr) { const grpc::string kIpv6("ipv6:[::1]:"); const grpc::string kIpv4MappedIpv6("ipv6:[::ffff:127.0.0.1]:"); @@ -212,137 +179,6 @@ class Proxy : public ::grpc::testing::EchoTestService::Service { std::unique_ptr< ::grpc::testing::EchoTestService::Stub> stub_; }; -class TestServiceImpl : public ::grpc::testing::EchoTestService::Service { - public: - TestServiceImpl() : signal_client_(false), host_() {} - explicit TestServiceImpl(const grpc::string& host) - : signal_client_(false), host_(new grpc::string(host)) {} - - Status Echo(ServerContext* context, const EchoRequest* request, - EchoResponse* response) GRPC_OVERRIDE { - 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 { - 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->has_param() && - (request->param().expected_client_identity().length() > 0 || - request->param().check_auth_context())) { - CheckServerAuthContext(context, - 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; - } - - // Unimplemented is left unimplemented to test the returned error. - - Status RequestStream(ServerContext* context, - ServerReader<EchoRequest>* reader, - EchoResponse* response) GRPC_OVERRIDE { - EchoRequest request; - response->set_message(""); - int cancel_after_reads = 0; - const std::multimap<grpc::string_ref, grpc::string_ref>& - client_initial_metadata = context->client_metadata(); - if (client_initial_metadata.find(kServerCancelAfterReads) != - client_initial_metadata.end()) { - std::istringstream iss(ToString( - client_initial_metadata.find(kServerCancelAfterReads)->second)); - iss >> cancel_after_reads; - gpr_log(GPR_INFO, "cancel_after_reads %d", cancel_after_reads); - } - while (reader->Read(&request)) { - if (cancel_after_reads == 1) { - gpr_log(GPR_INFO, "return cancel status"); - return Status::CANCELLED; - } else if (cancel_after_reads > 0) { - cancel_after_reads--; - } - response->mutable_message()->append(request.message()); - } - return Status::OK; - } - - // Return 3 messages. - // TODO(yangg) make it generic by adding a parameter into EchoRequest - Status ResponseStream(ServerContext* context, const EchoRequest* request, - ServerWriter<EchoResponse>* writer) GRPC_OVERRIDE { - EchoResponse response; - response.set_message(request->message() + "0"); - writer->Write(response); - response.set_message(request->message() + "1"); - writer->Write(response); - response.set_message(request->message() + "2"); - writer->Write(response); - - return Status::OK; - } - - Status BidiStream(ServerContext* context, - ServerReaderWriter<EchoResponse, EchoRequest>* stream) - GRPC_OVERRIDE { - EchoRequest request; - EchoResponse response; - while (stream->Read(&request)) { - gpr_log(GPR_INFO, "recv msg %s", request.message().c_str()); - response.set_message(request.message()); - stream->Write(response); - } - return Status::OK; - } - - bool signal_client() { - std::unique_lock<std::mutex> lock(mu_); - return signal_client_; - } - - private: - bool signal_client_; - std::mutex mu_; - std::unique_ptr<grpc::string> host_; -}; - class TestServiceImplDupPkg : public ::grpc::testing::duplicate::EchoTestService::Service { public: diff --git a/test/cpp/end2end/generic_end2end_test.cc b/test/cpp/end2end/generic_end2end_test.cc index c5d9de3f29..4e6d50ea80 100644 --- a/test/cpp/end2end/generic_end2end_test.cc +++ b/test/cpp/end2end/generic_end2end_test.cc @@ -51,6 +51,7 @@ #include "src/proto/grpc/testing/echo.grpc.pb.h" #include "test/core/util/port.h" #include "test/core/util/test_config.h" +#include "test/cpp/util/byte_buffer_proto_helper.h" using grpc::testing::EchoRequest; using grpc::testing::EchoResponse; @@ -70,29 +71,9 @@ void verify_ok(CompletionQueue* cq, int i, bool expect_ok) { EXPECT_EQ(tag(i), got_tag); } -bool ParseFromByteBuffer(ByteBuffer* buffer, grpc::protobuf::Message* message) { - std::vector<Slice> slices; - buffer->Dump(&slices); - grpc::string buf; - buf.reserve(buffer->Length()); - for (auto s = slices.begin(); s != slices.end(); s++) { - buf.append(reinterpret_cast<const char*>(s->begin()), s->size()); - } - return message->ParseFromString(buf); -} - -std::unique_ptr<ByteBuffer> SerializeToByteBuffer( - grpc::protobuf::Message* message) { - grpc::string buf; - message->SerializeToString(&buf); - gpr_slice s = gpr_slice_from_copied_string(buf.c_str()); - Slice slice(s, Slice::STEAL_REF); - return std::unique_ptr<ByteBuffer>(new ByteBuffer(&slice, 1)); -} - class GenericEnd2endTest : public ::testing::Test { protected: - GenericEnd2endTest() : generic_service_("*"), server_host_("localhost") {} + GenericEnd2endTest() : server_host_("localhost") {} void SetUp() GRPC_OVERRIDE { int port = grpc_pick_unused_port_or_die(); diff --git a/test/cpp/end2end/hybrid_end2end_test.cc b/test/cpp/end2end/hybrid_end2end_test.cc new file mode 100644 index 0000000000..dcee1f47a5 --- /dev/null +++ b/test/cpp/end2end/hybrid_end2end_test.cc @@ -0,0 +1,558 @@ +/* + * + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <memory> +#include <thread> + +#include <grpc++/channel.h> +#include <grpc++/client_context.h> +#include <grpc++/create_channel.h> +#include <grpc++/generic/async_generic_service.h> +#include <grpc++/server.h> +#include <grpc++/server_builder.h> +#include <grpc++/server_context.h> +#include <grpc/grpc.h> +#include <gtest/gtest.h> + +#include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h" +#include "src/proto/grpc/testing/echo.grpc.pb.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" +#include "test/cpp/end2end/test_service_impl.h" +#include "test/cpp/util/byte_buffer_proto_helper.h" + +namespace grpc { +namespace testing { + +namespace { + +void* tag(int i) { return (void*)(intptr_t)i; } + +bool VerifyReturnSuccess(CompletionQueue* cq, int i) { + void* got_tag; + bool ok; + EXPECT_TRUE(cq->Next(&got_tag, &ok)); + EXPECT_EQ(tag(i), got_tag); + return ok; +} + +void Verify(CompletionQueue* cq, int i, bool expect_ok) { + EXPECT_EQ(expect_ok, VerifyReturnSuccess(cq, i)); +} + +// Handlers to handle async request at a server. To be run in a separate thread. +template <class Service> +void HandleEcho(Service* service, ServerCompletionQueue* cq, bool dup_service) { + ServerContext srv_ctx; + grpc::ServerAsyncResponseWriter<EchoResponse> response_writer(&srv_ctx); + EchoRequest recv_request; + EchoResponse send_response; + service->RequestEcho(&srv_ctx, &recv_request, &response_writer, cq, cq, + tag(1)); + Verify(cq, 1, true); + send_response.set_message(recv_request.message()); + if (dup_service) { + send_response.mutable_message()->append("_dup"); + } + response_writer.Finish(send_response, Status::OK, tag(2)); + Verify(cq, 2, true); +} + +template <class Service> +void HandleClientStreaming(Service* service, ServerCompletionQueue* cq) { + ServerContext srv_ctx; + EchoRequest recv_request; + EchoResponse send_response; + ServerAsyncReader<EchoResponse, EchoRequest> srv_stream(&srv_ctx); + service->RequestRequestStream(&srv_ctx, &srv_stream, cq, cq, tag(1)); + Verify(cq, 1, true); + int i = 1; + do { + i++; + send_response.mutable_message()->append(recv_request.message()); + srv_stream.Read(&recv_request, tag(i)); + } while (VerifyReturnSuccess(cq, i)); + srv_stream.Finish(send_response, Status::OK, tag(100)); + Verify(cq, 100, true); +} + +template <class Service> +void HandleServerStreaming(Service* service, ServerCompletionQueue* cq) { + ServerContext srv_ctx; + EchoRequest recv_request; + EchoResponse send_response; + ServerAsyncWriter<EchoResponse> srv_stream(&srv_ctx); + service->RequestResponseStream(&srv_ctx, &recv_request, &srv_stream, cq, cq, + tag(1)); + Verify(cq, 1, true); + send_response.set_message(recv_request.message() + "0"); + srv_stream.Write(send_response, tag(2)); + Verify(cq, 2, true); + send_response.set_message(recv_request.message() + "1"); + srv_stream.Write(send_response, tag(3)); + Verify(cq, 3, true); + send_response.set_message(recv_request.message() + "2"); + srv_stream.Write(send_response, tag(4)); + Verify(cq, 4, true); + srv_stream.Finish(Status::OK, tag(5)); + Verify(cq, 5, true); +} + +void HandleGenericEcho(GenericServerAsyncReaderWriter* stream, + CompletionQueue* cq) { + ByteBuffer recv_buffer; + stream->Read(&recv_buffer, tag(2)); + Verify(cq, 2, true); + EchoRequest recv_request; + EXPECT_TRUE(ParseFromByteBuffer(&recv_buffer, &recv_request)); + EchoResponse send_response; + send_response.set_message(recv_request.message()); + auto send_buffer = SerializeToByteBuffer(&send_response); + stream->Write(*send_buffer, tag(3)); + Verify(cq, 3, true); + stream->Finish(Status::OK, tag(4)); + Verify(cq, 4, true); +} + +void HandleGenericRequestStream(GenericServerAsyncReaderWriter* stream, + CompletionQueue* cq) { + ByteBuffer recv_buffer; + EchoRequest recv_request; + EchoResponse send_response; + int i = 1; + while (true) { + i++; + stream->Read(&recv_buffer, tag(i)); + if (!VerifyReturnSuccess(cq, i)) { + break; + } + EXPECT_TRUE(ParseFromByteBuffer(&recv_buffer, &recv_request)); + send_response.mutable_message()->append(recv_request.message()); + } + auto send_buffer = SerializeToByteBuffer(&send_response); + stream->Write(*send_buffer, tag(99)); + Verify(cq, 99, true); + stream->Finish(Status::OK, tag(100)); + Verify(cq, 100, true); +} + +// Request and handle one generic call. +void HandleGenericCall(AsyncGenericService* service, + ServerCompletionQueue* cq) { + GenericServerContext srv_ctx; + GenericServerAsyncReaderWriter stream(&srv_ctx); + service->RequestCall(&srv_ctx, &stream, cq, cq, tag(1)); + Verify(cq, 1, true); + if (srv_ctx.method() == "/grpc.testing.EchoTestService/Echo") { + HandleGenericEcho(&stream, cq); + } else if (srv_ctx.method() == + "/grpc.testing.EchoTestService/RequestStream") { + HandleGenericRequestStream(&stream, cq); + } else { // other methods not handled yet. + gpr_log(GPR_ERROR, "method: %s", srv_ctx.method().c_str()); + GPR_ASSERT(0); + } +} + +class TestServiceImplDupPkg + : public ::grpc::testing::duplicate::EchoTestService::Service { + public: + Status Echo(ServerContext* context, const EchoRequest* request, + EchoResponse* response) GRPC_OVERRIDE { + response->set_message(request->message() + "_dup"); + return Status::OK; + } +}; + +class HybridEnd2endTest : public ::testing::Test { + protected: + HybridEnd2endTest() {} + + void SetUpServer(::grpc::Service* service1, ::grpc::Service* service2, + AsyncGenericService* generic_service) { + int port = grpc_pick_unused_port_or_die(); + server_address_ << "localhost:" << port; + + // Setup server + ServerBuilder builder; + builder.AddListeningPort(server_address_.str(), + grpc::InsecureServerCredentials()); + builder.RegisterService(service1); + if (service2) { + builder.RegisterService(service2); + } + if (generic_service) { + builder.RegisterAsyncGenericService(generic_service); + } + // Create a separate cq for each potential handler. + for (int i = 0; i < 5; i++) { + cqs_.push_back(std::move(builder.AddCompletionQueue())); + } + server_ = builder.BuildAndStart(); + } + + void TearDown() GRPC_OVERRIDE { + if (server_) { + server_->Shutdown(); + } + void* ignored_tag; + bool ignored_ok; + for (auto it = cqs_.begin(); it != cqs_.end(); ++it) { + (*it)->Shutdown(); + while ((*it)->Next(&ignored_tag, &ignored_ok)) + ; + } + } + + void ResetStub() { + std::shared_ptr<Channel> channel = + CreateChannel(server_address_.str(), InsecureChannelCredentials()); + stub_ = grpc::testing::EchoTestService::NewStub(channel); + } + + // Test all rpc methods. + void TestAllMethods() { + SendEcho(); + SendSimpleClientStreaming(); + SendSimpleServerStreaming(); + SendBidiStreaming(); + } + + void SendEcho() { + EchoRequest send_request; + EchoResponse recv_response; + ClientContext cli_ctx; + send_request.set_message("Hello"); + Status recv_status = stub_->Echo(&cli_ctx, send_request, &recv_response); + EXPECT_EQ(send_request.message(), recv_response.message()); + EXPECT_TRUE(recv_status.ok()); + } + + void SendEchoToDupService() { + std::shared_ptr<Channel> channel = + CreateChannel(server_address_.str(), InsecureChannelCredentials()); + auto stub = grpc::testing::duplicate::EchoTestService::NewStub(channel); + EchoRequest send_request; + EchoResponse recv_response; + ClientContext cli_ctx; + send_request.set_message("Hello"); + Status recv_status = stub->Echo(&cli_ctx, send_request, &recv_response); + EXPECT_EQ(send_request.message() + "_dup", recv_response.message()); + EXPECT_TRUE(recv_status.ok()); + } + + void SendSimpleClientStreaming() { + EchoRequest send_request; + EchoResponse recv_response; + grpc::string expected_message; + ClientContext cli_ctx; + send_request.set_message("Hello"); + auto stream = stub_->RequestStream(&cli_ctx, &recv_response); + for (int i = 0; i < 5; i++) { + EXPECT_TRUE(stream->Write(send_request)); + expected_message.append(send_request.message()); + } + stream->WritesDone(); + Status recv_status = stream->Finish(); + EXPECT_EQ(expected_message, recv_response.message()); + EXPECT_TRUE(recv_status.ok()); + } + + void SendSimpleServerStreaming() { + EchoRequest request; + EchoResponse response; + ClientContext context; + request.set_message("hello"); + + auto stream = stub_->ResponseStream(&context, request); + EXPECT_TRUE(stream->Read(&response)); + EXPECT_EQ(response.message(), request.message() + "0"); + EXPECT_TRUE(stream->Read(&response)); + EXPECT_EQ(response.message(), request.message() + "1"); + EXPECT_TRUE(stream->Read(&response)); + EXPECT_EQ(response.message(), request.message() + "2"); + EXPECT_FALSE(stream->Read(&response)); + + Status s = stream->Finish(); + EXPECT_TRUE(s.ok()); + } + + void SendBidiStreaming() { + EchoRequest request; + EchoResponse response; + ClientContext context; + grpc::string msg("hello"); + + auto stream = stub_->BidiStream(&context); + + request.set_message(msg + "0"); + EXPECT_TRUE(stream->Write(request)); + EXPECT_TRUE(stream->Read(&response)); + EXPECT_EQ(response.message(), request.message()); + + request.set_message(msg + "1"); + EXPECT_TRUE(stream->Write(request)); + EXPECT_TRUE(stream->Read(&response)); + EXPECT_EQ(response.message(), request.message()); + + request.set_message(msg + "2"); + EXPECT_TRUE(stream->Write(request)); + EXPECT_TRUE(stream->Read(&response)); + EXPECT_EQ(response.message(), request.message()); + + stream->WritesDone(); + EXPECT_FALSE(stream->Read(&response)); + EXPECT_FALSE(stream->Read(&response)); + + Status s = stream->Finish(); + EXPECT_TRUE(s.ok()); + } + + std::vector<std::unique_ptr<ServerCompletionQueue> > cqs_; + std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_; + std::unique_ptr<Server> server_; + std::ostringstream server_address_; +}; + +TEST_F(HybridEnd2endTest, AsyncEcho) { + EchoTestService::WithAsyncMethod_Echo<TestServiceImpl> service; + SetUpServer(&service, nullptr, nullptr); + ResetStub(); + std::thread echo_handler_thread( + [this, &service] { HandleEcho(&service, cqs_[0].get(), false); }); + TestAllMethods(); + echo_handler_thread.join(); +} + +TEST_F(HybridEnd2endTest, AsyncEchoRequestStream) { + EchoTestService::WithAsyncMethod_RequestStream< + EchoTestService::WithAsyncMethod_Echo<TestServiceImpl> > service; + SetUpServer(&service, nullptr, nullptr); + ResetStub(); + std::thread echo_handler_thread( + [this, &service] { HandleEcho(&service, cqs_[0].get(), false); }); + std::thread request_stream_handler_thread( + [this, &service] { HandleClientStreaming(&service, cqs_[1].get()); }); + TestAllMethods(); + echo_handler_thread.join(); + request_stream_handler_thread.join(); +} + +TEST_F(HybridEnd2endTest, AsyncRequestStreamResponseStream) { + EchoTestService::WithAsyncMethod_RequestStream< + EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl> > + service; + SetUpServer(&service, nullptr, nullptr); + ResetStub(); + std::thread response_stream_handler_thread( + [this, &service] { HandleServerStreaming(&service, cqs_[0].get()); }); + std::thread request_stream_handler_thread( + [this, &service] { HandleClientStreaming(&service, cqs_[1].get()); }); + TestAllMethods(); + response_stream_handler_thread.join(); + request_stream_handler_thread.join(); +} + +// Add a second service with one sync method. +TEST_F(HybridEnd2endTest, AsyncRequestStreamResponseStream_SyncDupService) { + EchoTestService::WithAsyncMethod_RequestStream< + EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl> > + service; + TestServiceImplDupPkg dup_service; + SetUpServer(&service, &dup_service, nullptr); + ResetStub(); + std::thread response_stream_handler_thread( + [this, &service] { HandleServerStreaming(&service, cqs_[0].get()); }); + std::thread request_stream_handler_thread( + [this, &service] { HandleClientStreaming(&service, cqs_[1].get()); }); + TestAllMethods(); + SendEchoToDupService(); + response_stream_handler_thread.join(); + request_stream_handler_thread.join(); +} + +// Add a second service with one async method. +TEST_F(HybridEnd2endTest, AsyncRequestStreamResponseStream_AsyncDupService) { + EchoTestService::WithAsyncMethod_RequestStream< + EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl> > + service; + duplicate::EchoTestService::AsyncService dup_service; + SetUpServer(&service, &dup_service, nullptr); + ResetStub(); + std::thread response_stream_handler_thread( + [this, &service] { HandleServerStreaming(&service, cqs_[0].get()); }); + std::thread request_stream_handler_thread( + [this, &service] { HandleClientStreaming(&service, cqs_[1].get()); }); + std::thread echo_handler_thread( + [this, &dup_service] { HandleEcho(&dup_service, cqs_[2].get(), true); }); + TestAllMethods(); + SendEchoToDupService(); + response_stream_handler_thread.join(); + request_stream_handler_thread.join(); + echo_handler_thread.join(); +} + +TEST_F(HybridEnd2endTest, GenericEcho) { + EchoTestService::WithGenericMethod_Echo<TestServiceImpl> service; + AsyncGenericService generic_service; + SetUpServer(&service, nullptr, &generic_service); + ResetStub(); + std::thread generic_handler_thread([this, &generic_service] { + HandleGenericCall(&generic_service, cqs_[0].get()); + }); + TestAllMethods(); + generic_handler_thread.join(); +} + +TEST_F(HybridEnd2endTest, GenericEchoAsyncRequestStream) { + EchoTestService::WithAsyncMethod_RequestStream< + EchoTestService::WithGenericMethod_Echo<TestServiceImpl> > service; + AsyncGenericService generic_service; + SetUpServer(&service, nullptr, &generic_service); + ResetStub(); + std::thread generic_handler_thread([this, &generic_service] { + HandleGenericCall(&generic_service, cqs_[0].get()); + }); + std::thread request_stream_handler_thread( + [this, &service] { HandleClientStreaming(&service, cqs_[1].get()); }); + TestAllMethods(); + generic_handler_thread.join(); + request_stream_handler_thread.join(); +} + +// Add a second service with one sync method. +TEST_F(HybridEnd2endTest, GenericEchoAsyncRequestStream_SyncDupService) { + EchoTestService::WithAsyncMethod_RequestStream< + EchoTestService::WithGenericMethod_Echo<TestServiceImpl> > + service; + AsyncGenericService generic_service; + TestServiceImplDupPkg dup_service; + SetUpServer(&service, &dup_service, &generic_service); + ResetStub(); + std::thread generic_handler_thread([this, &generic_service] { + HandleGenericCall(&generic_service, cqs_[0].get()); + }); + std::thread request_stream_handler_thread( + [this, &service] { HandleClientStreaming(&service, cqs_[1].get()); }); + TestAllMethods(); + SendEchoToDupService(); + generic_handler_thread.join(); + request_stream_handler_thread.join(); +} + +// Add a second service with one async method. +TEST_F(HybridEnd2endTest, GenericEchoAsyncRequestStream_AsyncDupService) { + EchoTestService::WithAsyncMethod_RequestStream< + EchoTestService::WithGenericMethod_Echo<TestServiceImpl> > + service; + AsyncGenericService generic_service; + duplicate::EchoTestService::AsyncService dup_service; + SetUpServer(&service, &dup_service, &generic_service); + ResetStub(); + std::thread generic_handler_thread([this, &generic_service] { + HandleGenericCall(&generic_service, cqs_[0].get()); + }); + std::thread request_stream_handler_thread( + [this, &service] { HandleClientStreaming(&service, cqs_[1].get()); }); + std::thread echo_handler_thread( + [this, &dup_service] { HandleEcho(&dup_service, cqs_[2].get(), true); }); + TestAllMethods(); + SendEchoToDupService(); + generic_handler_thread.join(); + request_stream_handler_thread.join(); + echo_handler_thread.join(); +} + +TEST_F(HybridEnd2endTest, GenericEchoAsyncRequestStreamResponseStream) { + EchoTestService::WithAsyncMethod_RequestStream< + EchoTestService::WithGenericMethod_Echo< + EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl> > > + service; + AsyncGenericService generic_service; + SetUpServer(&service, nullptr, &generic_service); + ResetStub(); + std::thread generic_handler_thread([this, &generic_service] { + HandleGenericCall(&generic_service, cqs_[0].get()); + }); + std::thread request_stream_handler_thread( + [this, &service] { HandleClientStreaming(&service, cqs_[1].get()); }); + std::thread response_stream_handler_thread( + [this, &service] { HandleServerStreaming(&service, cqs_[2].get()); }); + TestAllMethods(); + generic_handler_thread.join(); + request_stream_handler_thread.join(); + response_stream_handler_thread.join(); +} + +TEST_F(HybridEnd2endTest, GenericEchoRequestStreamAsyncResponseStream) { + EchoTestService::WithGenericMethod_RequestStream< + EchoTestService::WithGenericMethod_Echo< + EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl> > > + service; + AsyncGenericService generic_service; + SetUpServer(&service, nullptr, &generic_service); + ResetStub(); + std::thread generic_handler_thread([this, &generic_service] { + HandleGenericCall(&generic_service, cqs_[0].get()); + }); + std::thread generic_handler_thread2([this, &generic_service] { + HandleGenericCall(&generic_service, cqs_[1].get()); + }); + std::thread response_stream_handler_thread( + [this, &service] { HandleServerStreaming(&service, cqs_[2].get()); }); + TestAllMethods(); + generic_handler_thread.join(); + generic_handler_thread2.join(); + response_stream_handler_thread.join(); +} + +// If WithGenericMethod is called and no generic service is registered, the +// server will fail to build. +TEST_F(HybridEnd2endTest, GenericMethodWithoutGenericService) { + EchoTestService::WithGenericMethod_RequestStream< + EchoTestService::WithGenericMethod_Echo< + EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl> > > + service; + SetUpServer(&service, nullptr, nullptr); + EXPECT_EQ(nullptr, server_.get()); +} + +} // namespace +} // namespace testing +} // namespace grpc + +int main(int argc, char** argv) { + grpc_test_init(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/cpp/end2end/test_service_impl.cc b/test/cpp/end2end/test_service_impl.cc new file mode 100644 index 0000000000..c9a32ecf5a --- /dev/null +++ b/test/cpp/end2end/test_service_impl.cc @@ -0,0 +1,199 @@ +/* + * + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "test/cpp/end2end/test_service_impl.h" + +#include <grpc++/security/credentials.h> +#include <grpc++/server_context.h> +#include <grpc/grpc.h> +#include <gtest/gtest.h> + +#include "src/proto/grpc/testing/echo.grpc.pb.h" +#include "test/cpp/util/string_ref_helper.h" + +using std::chrono::system_clock; + +namespace grpc { +namespace testing { +namespace { + +// When echo_deadline is requested, deadline seen in the ServerContext is set in +// the response in seconds. +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_client_identity) { + std::shared_ptr<const AuthContext> auth_ctx = context->auth_context(); + std::vector<grpc::string_ref> ssl = + auth_ctx->FindPropertyValues("transport_security_type"); + EXPECT_EQ(1u, ssl.size()); + EXPECT_EQ("ssl", ToString(ssl[0])); + if (expected_client_identity.length() == 0) { + 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]); + } +} +} // namespace + +Status TestServiceImpl::Echo(ServerContext* context, const EchoRequest* request, + EchoResponse* response) { + 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 { + 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->has_param() && + (request->param().expected_client_identity().length() > 0 || + request->param().check_auth_context())) { + CheckServerAuthContext(context, + 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; +} + +// Unimplemented is left unimplemented to test the returned error. + +Status TestServiceImpl::RequestStream(ServerContext* context, + ServerReader<EchoRequest>* reader, + EchoResponse* response) { + EchoRequest request; + response->set_message(""); + int cancel_after_reads = 0; + const std::multimap<grpc::string_ref, grpc::string_ref>& + client_initial_metadata = context->client_metadata(); + if (client_initial_metadata.find(kServerCancelAfterReads) != + client_initial_metadata.end()) { + std::istringstream iss(ToString( + client_initial_metadata.find(kServerCancelAfterReads)->second)); + iss >> cancel_after_reads; + gpr_log(GPR_INFO, "cancel_after_reads %d", cancel_after_reads); + } + while (reader->Read(&request)) { + if (cancel_after_reads == 1) { + gpr_log(GPR_INFO, "return cancel status"); + return Status::CANCELLED; + } else if (cancel_after_reads > 0) { + cancel_after_reads--; + } + response->mutable_message()->append(request.message()); + } + return Status::OK; +} + +// Return 3 messages. +// TODO(yangg) make it generic by adding a parameter into EchoRequest +Status TestServiceImpl::ResponseStream(ServerContext* context, + const EchoRequest* request, + ServerWriter<EchoResponse>* writer) { + EchoResponse response; + response.set_message(request->message() + "0"); + writer->Write(response); + response.set_message(request->message() + "1"); + writer->Write(response); + response.set_message(request->message() + "2"); + writer->Write(response); + + return Status::OK; +} + +Status TestServiceImpl::BidiStream( + ServerContext* context, + ServerReaderWriter<EchoResponse, EchoRequest>* stream) { + EchoRequest request; + EchoResponse response; + while (stream->Read(&request)) { + gpr_log(GPR_INFO, "recv msg %s", request.message().c_str()); + response.set_message(request.message()); + stream->Write(response); + } + return Status::OK; +} + +} // namespace testing +} // namespace grpc diff --git a/test/cpp/end2end/test_service_impl.h b/test/cpp/end2end/test_service_impl.h new file mode 100644 index 0000000000..2c35b5614c --- /dev/null +++ b/test/cpp/end2end/test_service_impl.h @@ -0,0 +1,85 @@ +/* + * + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef GRPC_TEST_CPP_END2END_TEST_SERVICE_IMPL_H +#define GRPC_TEST_CPP_END2END_TEST_SERVICE_IMPL_H + +#include <memory> +#include <mutex> + +#include <grpc++/server_context.h> +#include <grpc/grpc.h> + +#include "src/proto/grpc/testing/echo.grpc.pb.h" + +namespace grpc { +namespace testing { + +const char* const kServerCancelAfterReads = "cancel_after_reads"; + +class TestServiceImpl : public ::grpc::testing::EchoTestService::Service { + public: + TestServiceImpl() : signal_client_(false), host_() {} + explicit TestServiceImpl(const grpc::string& host) + : signal_client_(false), host_(new grpc::string(host)) {} + + Status Echo(ServerContext* context, const EchoRequest* request, + EchoResponse* response) GRPC_OVERRIDE; + + // Unimplemented is left unimplemented to test the returned error. + + Status RequestStream(ServerContext* context, + ServerReader<EchoRequest>* reader, + EchoResponse* response) GRPC_OVERRIDE; + + Status ResponseStream(ServerContext* context, const EchoRequest* request, + ServerWriter<EchoResponse>* writer) GRPC_OVERRIDE; + + Status BidiStream(ServerContext* context, + ServerReaderWriter<EchoResponse, EchoRequest>* stream) + GRPC_OVERRIDE; + + bool signal_client() { + std::unique_lock<std::mutex> lock(mu_); + return signal_client_; + } + + private: + bool signal_client_; + std::mutex mu_; + std::unique_ptr<grpc::string> host_; +}; + +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_END2END_TEST_SERVICE_IMPL_H diff --git a/test/cpp/qps/client_async.cc b/test/cpp/qps/client_async.cc index f270cd0987..d02769d8e6 100644 --- a/test/cpp/qps/client_async.cc +++ b/test/cpp/qps/client_async.cc @@ -49,10 +49,10 @@ #include <grpc/support/histogram.h> #include <grpc/support/log.h> +#include "src/proto/grpc/testing/services.grpc.pb.h" #include "test/cpp/qps/client.h" #include "test/cpp/qps/timer.h" #include "test/cpp/util/create_test_channel.h" -#include "src/proto/grpc/testing/services.grpc.pb.h" namespace grpc { namespace testing { diff --git a/test/cpp/qps/server_async.cc b/test/cpp/qps/server_async.cc index d530dac86b..32a8fc48e6 100644 --- a/test/cpp/qps/server_async.cc +++ b/test/cpp/qps/server_async.cc @@ -350,7 +350,7 @@ class AsyncQpsServerTest : public Server { static void RegisterBenchmarkService(ServerBuilder *builder, BenchmarkService::AsyncService *service) { - builder->RegisterAsyncService(service); + builder->RegisterService(service); } static void RegisterGenericService(ServerBuilder *builder, grpc::AsyncGenericService *service) { diff --git a/test/cpp/util/byte_buffer_proto_helper.cc b/test/cpp/util/byte_buffer_proto_helper.cc new file mode 100644 index 0000000000..2512c9bdf8 --- /dev/null +++ b/test/cpp/util/byte_buffer_proto_helper.cc @@ -0,0 +1,60 @@ +/* + * + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "test/cpp/util/byte_buffer_proto_helper.h" + +namespace grpc { +namespace testing { + +bool ParseFromByteBuffer(ByteBuffer* buffer, grpc::protobuf::Message* message) { + std::vector<Slice> slices; + buffer->Dump(&slices); + grpc::string buf; + buf.reserve(buffer->Length()); + for (auto s = slices.begin(); s != slices.end(); s++) { + buf.append(reinterpret_cast<const char*>(s->begin()), s->size()); + } + return message->ParseFromString(buf); +} + +std::unique_ptr<ByteBuffer> SerializeToByteBuffer( + grpc::protobuf::Message* message) { + grpc::string buf; + message->SerializeToString(&buf); + gpr_slice s = gpr_slice_from_copied_string(buf.c_str()); + Slice slice(s, Slice::STEAL_REF); + return std::unique_ptr<ByteBuffer>(new ByteBuffer(&slice, 1)); +} + +} // namespace testing +} // namespace grpc diff --git a/test/cpp/util/byte_buffer_proto_helper.h b/test/cpp/util/byte_buffer_proto_helper.h new file mode 100644 index 0000000000..42cea59e33 --- /dev/null +++ b/test/cpp/util/byte_buffer_proto_helper.h @@ -0,0 +1,53 @@ +/* + * + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_TEST_CPP_UTIL_BYTE_BUFFER_PROTO_HELPER_H +#define GRPC_TEST_CPP_UTIL_BYTE_BUFFER_PROTO_HELPER_H + +#include <memory> + +#include <grpc++/support/byte_buffer.h> +#include <grpc++/support/config_protobuf.h> + +namespace grpc { +namespace testing { + +bool ParseFromByteBuffer(ByteBuffer* buffer, grpc::protobuf::Message* message); + +std::unique_ptr<ByteBuffer> SerializeToByteBuffer( + grpc::protobuf::Message* message); + +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_UTIL_BYTE_BUFFER_PROTO_HELPER_H diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++ index 500d110ad0..ebe8a564f8 100644 --- a/tools/doxygen/Doxyfile.c++ +++ b/tools/doxygen/Doxyfile.c++ @@ -770,6 +770,7 @@ include/grpc++/grpc++.h \ include/grpc++/impl/call.h \ include/grpc++/impl/client_unary_call.h \ include/grpc++/impl/grpc_library.h \ +include/grpc++/impl/method_handler_impl.h \ include/grpc++/impl/proto_utils.h \ include/grpc++/impl/rpc_method.h \ include/grpc++/impl/rpc_service_method.h \ diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index ba1dec0d38..b9961c034c 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -770,6 +770,7 @@ include/grpc++/grpc++.h \ include/grpc++/impl/call.h \ include/grpc++/impl/client_unary_call.h \ include/grpc++/impl/grpc_library.h \ +include/grpc++/impl/method_handler_impl.h \ include/grpc++/impl/proto_utils.h \ include/grpc++/impl/rpc_method.h \ include/grpc++/impl/rpc_service_method.h \ diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json index 3e42c59ed3..0b4ff0d6f7 100644 --- a/tools/run_tests/sources_and_headers.json +++ b/tools/run_tests/sources_and_headers.json @@ -1608,6 +1608,22 @@ "gpr_test_util", "grpc", "grpc++", + "grpc++_test_util", + "grpc_test_util" + ], + "headers": [], + "language": "c++", + "name": "hybrid_end2end_test", + "src": [ + "test/cpp/end2end/hybrid_end2end_test.cc" + ] + }, + { + "deps": [ + "gpr", + "gpr_test_util", + "grpc", + "grpc++", "grpc++_test_config", "grpc++_test_util", "grpc_test_util", @@ -3829,6 +3845,7 @@ "include/grpc++/impl/call.h", "include/grpc++/impl/client_unary_call.h", "include/grpc++/impl/grpc_library.h", + "include/grpc++/impl/method_handler_impl.h", "include/grpc++/impl/proto_utils.h", "include/grpc++/impl/rpc_method.h", "include/grpc++/impl/rpc_service_method.h", @@ -3883,6 +3900,7 @@ "include/grpc++/impl/call.h", "include/grpc++/impl/client_unary_call.h", "include/grpc++/impl/grpc_library.h", + "include/grpc++/impl/method_handler_impl.h", "include/grpc++/impl/proto_utils.h", "include/grpc++/impl/rpc_method.h", "include/grpc++/impl/rpc_service_method.h", @@ -3981,6 +3999,8 @@ "src/proto/grpc/testing/echo.pb.h", "src/proto/grpc/testing/echo_messages.grpc.pb.h", "src/proto/grpc/testing/echo_messages.pb.h", + "test/cpp/end2end/test_service_impl.h", + "test/cpp/util/byte_buffer_proto_helper.h", "test/cpp/util/cli_call.h", "test/cpp/util/create_test_channel.h", "test/cpp/util/string_ref_helper.h", @@ -3989,6 +4009,10 @@ "language": "c++", "name": "grpc++_test_util", "src": [ + "test/cpp/end2end/test_service_impl.cc", + "test/cpp/end2end/test_service_impl.h", + "test/cpp/util/byte_buffer_proto_helper.cc", + "test/cpp/util/byte_buffer_proto_helper.h", "test/cpp/util/cli_call.cc", "test/cpp/util/cli_call.h", "test/cpp/util/create_test_channel.cc", @@ -4015,6 +4039,7 @@ "include/grpc++/impl/call.h", "include/grpc++/impl/client_unary_call.h", "include/grpc++/impl/grpc_library.h", + "include/grpc++/impl/method_handler_impl.h", "include/grpc++/impl/proto_utils.h", "include/grpc++/impl/rpc_method.h", "include/grpc++/impl/rpc_service_method.h", @@ -4066,6 +4091,7 @@ "include/grpc++/impl/call.h", "include/grpc++/impl/client_unary_call.h", "include/grpc++/impl/grpc_library.h", + "include/grpc++/impl/method_handler_impl.h", "include/grpc++/impl/proto_utils.h", "include/grpc++/impl/rpc_method.h", "include/grpc++/impl/rpc_service_method.h", diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json index 2757ce445d..509ba5bfe2 100644 --- a/tools/run_tests/tests.json +++ b/tools/run_tests/tests.json @@ -1864,6 +1864,25 @@ "ci_platforms": [ "linux", "mac", + "posix", + "windows" + ], + "exclude_configs": [], + "flaky": false, + "language": "c++", + "name": "hybrid_end2end_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ] + }, + { + "args": [], + "ci_platforms": [ + "linux", + "mac", "posix" ], "exclude_configs": [], diff --git a/vsprojects/vcxproj/grpc++/grpc++.vcxproj b/vsprojects/vcxproj/grpc++/grpc++.vcxproj index 886d415631..b9eea4be2f 100644 --- a/vsprojects/vcxproj/grpc++/grpc++.vcxproj +++ b/vsprojects/vcxproj/grpc++/grpc++.vcxproj @@ -268,6 +268,7 @@ <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\call.h" /> <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\client_unary_call.h" /> <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\grpc_library.h" /> + <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\method_handler_impl.h" /> <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\proto_utils.h" /> <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\rpc_method.h" /> <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\rpc_service_method.h" /> diff --git a/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters b/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters index abd39c7aa1..eb53819a01 100644 --- a/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters +++ b/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters @@ -129,6 +129,9 @@ <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\grpc_library.h"> <Filter>include\grpc++\impl</Filter> </ClInclude> + <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\method_handler_impl.h"> + <Filter>include\grpc++\impl</Filter> + </ClInclude> <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\proto_utils.h"> <Filter>include\grpc++\impl</Filter> </ClInclude> diff --git a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj index 299fdaf773..3d353716a9 100644 --- a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj +++ b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj @@ -147,6 +147,8 @@ </ItemDefinitionGroup> <ItemGroup> + <ClInclude Include="$(SolutionDir)\..\test\cpp\end2end\test_service_impl.h" /> + <ClInclude Include="$(SolutionDir)\..\test\cpp\util\byte_buffer_proto_helper.h" /> <ClInclude Include="$(SolutionDir)\..\test\cpp\util\cli_call.h" /> <ClInclude Include="$(SolutionDir)\..\test\cpp\util\create_test_channel.h" /> <ClInclude Include="$(SolutionDir)\..\test\cpp\util\string_ref_helper.h" /> @@ -177,6 +179,10 @@ </ClCompile> <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\duplicate\echo_duplicate.grpc.pb.h"> </ClInclude> + <ClCompile Include="$(SolutionDir)\..\test\cpp\end2end\test_service_impl.cc"> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\test\cpp\util\byte_buffer_proto_helper.cc"> + </ClCompile> <ClCompile Include="$(SolutionDir)\..\test\cpp\util\cli_call.cc"> </ClCompile> <ClCompile Include="$(SolutionDir)\..\test\cpp\util\create_test_channel.cc"> diff --git a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters index 70addd61b1..27ac6751b9 100644 --- a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters +++ b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters @@ -10,6 +10,12 @@ <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\duplicate\echo_duplicate.proto"> <Filter>src\proto\grpc\testing\duplicate</Filter> </ClCompile> + <ClCompile Include="$(SolutionDir)\..\test\cpp\end2end\test_service_impl.cc"> + <Filter>test\cpp\end2end</Filter> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\test\cpp\util\byte_buffer_proto_helper.cc"> + <Filter>test\cpp\util</Filter> + </ClCompile> <ClCompile Include="$(SolutionDir)\..\test\cpp\util\cli_call.cc"> <Filter>test\cpp\util</Filter> </ClCompile> @@ -24,6 +30,12 @@ </ClCompile> </ItemGroup> <ItemGroup> + <ClInclude Include="$(SolutionDir)\..\test\cpp\end2end\test_service_impl.h"> + <Filter>test\cpp\end2end</Filter> + </ClInclude> + <ClInclude Include="$(SolutionDir)\..\test\cpp\util\byte_buffer_proto_helper.h"> + <Filter>test\cpp\util</Filter> + </ClInclude> <ClInclude Include="$(SolutionDir)\..\test\cpp\util\cli_call.h"> <Filter>test\cpp\util</Filter> </ClInclude> @@ -60,6 +72,9 @@ <Filter Include="test\cpp"> <UniqueIdentifier>{793efaa7-370f-c34a-d347-31fc4e0630e2}</UniqueIdentifier> </Filter> + <Filter Include="test\cpp\end2end"> + <UniqueIdentifier>{1e6760f2-4cf7-1ecb-88a5-5faeec3c2150}</UniqueIdentifier> + </Filter> <Filter Include="test\cpp\util"> <UniqueIdentifier>{bbe1e5b7-f4f9-8e32-ce7c-8c21afcf39d8}</UniqueIdentifier> </Filter> diff --git a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj index 2193321913..dd1d1a85fe 100644 --- a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj +++ b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj @@ -268,6 +268,7 @@ <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\call.h" /> <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\client_unary_call.h" /> <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\grpc_library.h" /> + <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\method_handler_impl.h" /> <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\proto_utils.h" /> <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\rpc_method.h" /> <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\rpc_service_method.h" /> diff --git a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters index 073b181225..3d23dc4b44 100644 --- a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters +++ b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters @@ -114,6 +114,9 @@ <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\grpc_library.h"> <Filter>include\grpc++\impl</Filter> </ClInclude> + <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\method_handler_impl.h"> + <Filter>include\grpc++\impl</Filter> + </ClInclude> <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\proto_utils.h"> <Filter>include\grpc++\impl</Filter> </ClInclude> diff --git a/vsprojects/vcxproj/test/hybrid_end2end_test/hybrid_end2end_test.vcxproj b/vsprojects/vcxproj/test/hybrid_end2end_test/hybrid_end2end_test.vcxproj new file mode 100644 index 0000000000..7bdc6c6674 --- /dev/null +++ b/vsprojects/vcxproj/test/hybrid_end2end_test/hybrid_end2end_test.vcxproj @@ -0,0 +1,207 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" /> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{2DBA9954-A78A-6F68-5669-0370C6D6080C}</ProjectGuid> + <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected> + <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration"> + <PlatformToolset>v100</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration"> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration"> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration"> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="$(SolutionDir)\..\vsprojects\cpptest.props" /> + <Import Project="$(SolutionDir)\..\vsprojects\global.props" /> + <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" /> + <Import Project="$(SolutionDir)\..\vsprojects\protobuf.props" /> + <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" /> + <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)'=='Debug'"> + <TargetName>hybrid_end2end_test</TargetName> + <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib> + <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib> + <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl> + <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)'=='Release'"> + <TargetName>hybrid_end2end_test</TargetName> + <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib> + <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib> + <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl> + <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <TreatWarningAsError>true</TreatWarningAsError> + <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> + <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> + <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <TreatWarningAsError>true</TreatWarningAsError> + <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> + <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> + <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>MaxSpeed</Optimization> + <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <TreatWarningAsError>true</TreatWarningAsError> + <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> + <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> + <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>MaxSpeed</Optimization> + <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <TreatWarningAsError>true</TreatWarningAsError> + <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> + <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> + <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + + <ItemGroup> + <ClCompile Include="$(SolutionDir)\..\test\cpp\end2end\hybrid_end2end_test.cc"> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++_test_util\grpc++_test_util.vcxproj"> + <Project>{0BE77741-552A-929B-A497-4EF7ECE17A64}</Project> + </ProjectReference> + <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj"> + <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project> + </ProjectReference> + <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++\grpc++.vcxproj"> + <Project>{C187A093-A0FE-489D-A40A-6E33DE0F9FEB}</Project> + </ProjectReference> + <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj"> + <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project> + </ProjectReference> + <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj"> + <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project> + </ProjectReference> + <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj"> + <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" /> + <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" /> + <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" /> + <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" /> + </ImportGroup> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" /> + <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" /> + <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" /> + <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" /> + <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" /> + </Target> +</Project> + diff --git a/vsprojects/vcxproj/test/hybrid_end2end_test/hybrid_end2end_test.vcxproj.filters b/vsprojects/vcxproj/test/hybrid_end2end_test/hybrid_end2end_test.vcxproj.filters new file mode 100644 index 0000000000..ebb9753af1 --- /dev/null +++ b/vsprojects/vcxproj/test/hybrid_end2end_test/hybrid_end2end_test.vcxproj.filters @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="$(SolutionDir)\..\test\cpp\end2end\hybrid_end2end_test.cc"> + <Filter>test\cpp\end2end</Filter> + </ClCompile> + </ItemGroup> + + <ItemGroup> + <Filter Include="test"> + <UniqueIdentifier>{034a7201-59db-54c2-0af5-fd686ce948b6}</UniqueIdentifier> + </Filter> + <Filter Include="test\cpp"> + <UniqueIdentifier>{2bb7ef60-02e9-bb7c-6a37-4d8e2d8870ec}</UniqueIdentifier> + </Filter> + <Filter Include="test\cpp\end2end"> + <UniqueIdentifier>{d1b13ade-4b26-87da-a8a8-4c9766121e60}</UniqueIdentifier> + </Filter> + </ItemGroup> +</Project> + |